eufy-security-client 2.4.2 → 2.4.4
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/README.md +12 -0
- package/build/error.d.ts +57 -57
- package/build/error.js +155 -155
- package/build/eufysecurity.d.ts +162 -161
- package/build/eufysecurity.js +2104 -2091
- package/build/eufysecurity.js.map +1 -1
- package/build/http/api.d.ts +90 -90
- package/build/http/api.js +1407 -1407
- package/build/http/api.js.map +1 -1
- package/build/http/cache.d.ts +8 -8
- package/build/http/cache.js +33 -33
- package/build/http/const.d.ts +3 -3
- package/build/http/const.js +8545 -8545
- package/build/http/device.d.ts +360 -360
- package/build/http/device.js +2793 -2793
- package/build/http/device.js.map +1 -1
- package/build/http/error.d.ts +28 -28
- package/build/http/error.js +76 -76
- package/build/http/index.d.ts +10 -10
- package/build/http/index.js +29 -29
- package/build/http/interfaces.d.ts +202 -202
- package/build/http/interfaces.js +2 -2
- package/build/http/models.d.ts +561 -561
- package/build/http/models.js +2 -2
- package/build/http/parameter.d.ts +5 -5
- package/build/http/parameter.js +75 -75
- package/build/http/station.d.ts +292 -292
- package/build/http/station.js +6780 -6780
- package/build/http/station.js.map +1 -1
- package/build/http/types.d.ts +945 -945
- package/build/http/types.js +6070 -6070
- package/build/http/utils.d.ts +37 -37
- package/build/http/utils.js +370 -370
- package/build/index.d.ts +7 -7
- package/build/index.js +25 -25
- package/build/interfaces.d.ts +113 -113
- package/build/interfaces.js +2 -2
- package/build/mqtt/interface.d.ts +6 -6
- package/build/mqtt/interface.js +2 -2
- package/build/mqtt/model.d.ts +24 -24
- package/build/mqtt/model.js +2 -2
- package/build/mqtt/service.d.ts +30 -30
- package/build/mqtt/service.js +168 -168
- package/build/mqtt/service.js.map +1 -1
- package/build/p2p/ble.d.ts +47 -47
- package/build/p2p/ble.js +188 -188
- package/build/p2p/ble.js.map +1 -1
- package/build/p2p/error.d.ts +24 -24
- package/build/p2p/error.js +67 -67
- package/build/p2p/index.d.ts +8 -8
- package/build/p2p/index.js +27 -27
- package/build/p2p/interfaces.d.ts +162 -162
- package/build/p2p/interfaces.js +2 -2
- package/build/p2p/models.d.ts +146 -146
- package/build/p2p/models.js +2 -2
- package/build/p2p/session.d.ts +168 -168
- package/build/p2p/session.js +2087 -2087
- package/build/p2p/session.js.map +1 -1
- package/build/p2p/talkback.d.ts +10 -10
- package/build/p2p/talkback.js +22 -22
- package/build/p2p/types.d.ts +923 -923
- package/build/p2p/types.js +957 -957
- package/build/p2p/utils.d.ts +56 -56
- package/build/p2p/utils.js +653 -653
- package/build/push/client.d.ts +51 -51
- package/build/push/client.js +311 -311
- package/build/push/client.js.map +1 -1
- package/build/push/index.d.ts +5 -5
- package/build/push/index.js +24 -24
- package/build/push/interfaces.d.ts +19 -19
- package/build/push/interfaces.js +2 -2
- package/build/push/models.d.ts +292 -292
- package/build/push/models.js +30 -30
- package/build/push/parser.d.ts +28 -28
- package/build/push/parser.js +215 -215
- package/build/push/parser.js.map +1 -1
- package/build/push/service.d.ts +45 -45
- package/build/push/service.js +643 -643
- package/build/push/service.js.map +1 -1
- package/build/push/types.d.ts +176 -176
- package/build/push/types.js +192 -192
- package/build/push/utils.d.ts +7 -7
- package/build/push/utils.js +102 -102
- package/build/utils.d.ts +16 -13
- package/build/utils.js +207 -191
- package/build/utils.js.map +1 -1
- package/package.json +10 -10
package/build/push/client.js
CHANGED
|
@@ -1,312 +1,312 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.PushClient = void 0;
|
|
27
|
-
const Long = __importStar(require("long"));
|
|
28
|
-
const path = __importStar(require("path"));
|
|
29
|
-
const protobuf_typescript_1 = require("protobuf-typescript");
|
|
30
|
-
const tls = __importStar(require("tls"));
|
|
31
|
-
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
|
|
32
|
-
const ts_log_1 = require("ts-log");
|
|
33
|
-
const models_1 = require("./models");
|
|
34
|
-
const parser_1 = require("./parser");
|
|
35
|
-
const utils_1 = require("../utils");
|
|
36
|
-
class PushClient extends tiny_typed_emitter_1.TypedEmitter {
|
|
37
|
-
constructor(pushClientParser, auth, log = ts_log_1.dummyLogger) {
|
|
38
|
-
super();
|
|
39
|
-
this.HOST = "mtalk.google.com";
|
|
40
|
-
this.PORT = 5228;
|
|
41
|
-
this.MCS_VERSION = 41;
|
|
42
|
-
this.HEARTBEAT_INTERVAL = 5 * 60 * 1000;
|
|
43
|
-
this.loggedIn = false;
|
|
44
|
-
this.streamId = 0;
|
|
45
|
-
this.lastStreamIdReported = -1;
|
|
46
|
-
this.currentDelay = 0;
|
|
47
|
-
this.persistentIds = [];
|
|
48
|
-
this.log = log;
|
|
49
|
-
this.pushClientParser = pushClientParser;
|
|
50
|
-
this.auth = auth;
|
|
51
|
-
}
|
|
52
|
-
static async init(auth, log = ts_log_1.dummyLogger) {
|
|
53
|
-
this.proto = await (0, protobuf_typescript_1.load)(path.join(__dirname, "./proto/mcs.proto"));
|
|
54
|
-
const pushClientParser = await parser_1.PushClientParser.init(log);
|
|
55
|
-
return new PushClient(pushClientParser, auth, log);
|
|
56
|
-
}
|
|
57
|
-
initialize() {
|
|
58
|
-
this.loggedIn = false;
|
|
59
|
-
this.streamId = 0;
|
|
60
|
-
this.lastStreamIdReported = -1;
|
|
61
|
-
if (this.client) {
|
|
62
|
-
this.client.removeAllListeners();
|
|
63
|
-
this.client.destroy();
|
|
64
|
-
this.client = undefined;
|
|
65
|
-
}
|
|
66
|
-
this.pushClientParser.resetState();
|
|
67
|
-
if (this.reconnectTimeout) {
|
|
68
|
-
clearTimeout(this.reconnectTimeout);
|
|
69
|
-
this.reconnectTimeout = undefined;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
getPersistentIds() {
|
|
73
|
-
return this.persistentIds;
|
|
74
|
-
}
|
|
75
|
-
setPersistentIds(ids) {
|
|
76
|
-
this.persistentIds = ids;
|
|
77
|
-
}
|
|
78
|
-
connect() {
|
|
79
|
-
this.initialize();
|
|
80
|
-
this.pushClientParser.on("message", (message) => this.handleParsedMessage(message));
|
|
81
|
-
this.client = tls.connect(this.PORT, this.HOST, {
|
|
82
|
-
rejectUnauthorized: false,
|
|
83
|
-
});
|
|
84
|
-
this.client.setKeepAlive(true);
|
|
85
|
-
// For debugging purposes
|
|
86
|
-
//this.client.enableTrace();
|
|
87
|
-
this.client.on("connect", () => this.onSocketConnect());
|
|
88
|
-
this.client.on("close", () => this.onSocketClose());
|
|
89
|
-
this.client.on("error", (error) => this.onSocketError(error));
|
|
90
|
-
this.client.on("data", (newData) => this.onSocketData(newData));
|
|
91
|
-
this.client.write(this.buildLoginRequest());
|
|
92
|
-
}
|
|
93
|
-
buildLoginRequest() {
|
|
94
|
-
const androidId = this.auth.androidId;
|
|
95
|
-
const securityToken = this.auth.securityToken;
|
|
96
|
-
const LoginRequestType = PushClient.proto.lookupType("mcs_proto.LoginRequest");
|
|
97
|
-
const hexAndroidId = Long.fromString(androidId).toString(16);
|
|
98
|
-
const loginRequest = {
|
|
99
|
-
adaptiveHeartbeat: false,
|
|
100
|
-
authService: 2,
|
|
101
|
-
authToken: securityToken,
|
|
102
|
-
id: "chrome-63.0.3234.0",
|
|
103
|
-
domain: "mcs.android.com",
|
|
104
|
-
deviceId: `android-${hexAndroidId}`,
|
|
105
|
-
networkType: 1,
|
|
106
|
-
resource: androidId,
|
|
107
|
-
user: androidId,
|
|
108
|
-
useRmq2: true,
|
|
109
|
-
setting: [{ name: "new_vc", value: "1" }],
|
|
110
|
-
clientEvent: [],
|
|
111
|
-
receivedPersistentId: this.persistentIds,
|
|
112
|
-
};
|
|
113
|
-
const errorMessage = LoginRequestType.verify(loginRequest);
|
|
114
|
-
if (errorMessage) {
|
|
115
|
-
throw new Error(errorMessage);
|
|
116
|
-
}
|
|
117
|
-
const buffer = LoginRequestType.encodeDelimited(loginRequest).finish();
|
|
118
|
-
return Buffer.concat([Buffer.from([this.MCS_VERSION, models_1.MessageTag.LoginRequest]), buffer]);
|
|
119
|
-
}
|
|
120
|
-
buildHeartbeatPingRequest(stream_id) {
|
|
121
|
-
const heartbeatPingRequest = {};
|
|
122
|
-
if (stream_id) {
|
|
123
|
-
heartbeatPingRequest.last_stream_id_received = stream_id;
|
|
124
|
-
}
|
|
125
|
-
this.log.debug(`heartbeatPingRequest`, heartbeatPingRequest);
|
|
126
|
-
const HeartbeatPingRequestType = PushClient.proto.lookupType("mcs_proto.HeartbeatPing");
|
|
127
|
-
const errorMessage = HeartbeatPingRequestType.verify(heartbeatPingRequest);
|
|
128
|
-
if (errorMessage) {
|
|
129
|
-
throw new Error(errorMessage);
|
|
130
|
-
}
|
|
131
|
-
const buffer = HeartbeatPingRequestType.encodeDelimited(heartbeatPingRequest).finish();
|
|
132
|
-
return Buffer.concat([Buffer.from([models_1.MessageTag.HeartbeatPing]), buffer]);
|
|
133
|
-
}
|
|
134
|
-
buildHeartbeatAckRequest(stream_id, status) {
|
|
135
|
-
const heartbeatAckRequest = {};
|
|
136
|
-
if (stream_id && !status) {
|
|
137
|
-
heartbeatAckRequest.last_stream_id_received = stream_id;
|
|
138
|
-
}
|
|
139
|
-
else if (!stream_id && status) {
|
|
140
|
-
heartbeatAckRequest.status = status;
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
heartbeatAckRequest.last_stream_id_received = stream_id;
|
|
144
|
-
heartbeatAckRequest.status = status;
|
|
145
|
-
}
|
|
146
|
-
this.log.debug(`heartbeatAckRequest`, heartbeatAckRequest);
|
|
147
|
-
const HeartbeatAckRequestType = PushClient.proto.lookupType("mcs_proto.HeartbeatAck");
|
|
148
|
-
const errorMessage = HeartbeatAckRequestType.verify(heartbeatAckRequest);
|
|
149
|
-
if (errorMessage) {
|
|
150
|
-
throw new Error(errorMessage);
|
|
151
|
-
}
|
|
152
|
-
const buffer = HeartbeatAckRequestType.encodeDelimited(heartbeatAckRequest).finish();
|
|
153
|
-
return Buffer.concat([Buffer.from([models_1.MessageTag.HeartbeatAck]), buffer]);
|
|
154
|
-
}
|
|
155
|
-
onSocketData(newData) {
|
|
156
|
-
this.pushClientParser.handleData(newData);
|
|
157
|
-
}
|
|
158
|
-
onSocketConnect() {
|
|
159
|
-
//
|
|
160
|
-
}
|
|
161
|
-
onSocketClose() {
|
|
162
|
-
this.loggedIn = false;
|
|
163
|
-
if (this.heartbeatTimeout) {
|
|
164
|
-
clearTimeout(this.heartbeatTimeout);
|
|
165
|
-
this.heartbeatTimeout = undefined;
|
|
166
|
-
}
|
|
167
|
-
this.emit("close");
|
|
168
|
-
this.scheduleReconnect();
|
|
169
|
-
}
|
|
170
|
-
onSocketError(error) {
|
|
171
|
-
this.log.error(`onSocketError:`, error);
|
|
172
|
-
}
|
|
173
|
-
handleParsedMessage(message) {
|
|
174
|
-
this.resetCurrentDelay();
|
|
175
|
-
switch (message.tag) {
|
|
176
|
-
case models_1.MessageTag.DataMessageStanza:
|
|
177
|
-
this.log.debug(`DataMessageStanza`, message);
|
|
178
|
-
if (message.object && message.object.persistentId)
|
|
179
|
-
this.persistentIds.push(message.object.persistentId);
|
|
180
|
-
this.emit("message", this.convertPayloadMessage(message));
|
|
181
|
-
break;
|
|
182
|
-
case models_1.MessageTag.HeartbeatPing:
|
|
183
|
-
this.handleHeartbeatPing(message);
|
|
184
|
-
break;
|
|
185
|
-
case models_1.MessageTag.HeartbeatAck:
|
|
186
|
-
this.handleHeartbeatAck(message);
|
|
187
|
-
break;
|
|
188
|
-
case models_1.MessageTag.Close:
|
|
189
|
-
this.log.debug(`Close: Server requested close`, message);
|
|
190
|
-
break;
|
|
191
|
-
case models_1.MessageTag.LoginResponse:
|
|
192
|
-
this.log.debug("Login response: GCM -> logged in -> waiting for push messages...");
|
|
193
|
-
this.loggedIn = true;
|
|
194
|
-
this.persistentIds = [];
|
|
195
|
-
this.emit("connect");
|
|
196
|
-
this.heartbeatTimeout = setTimeout(() => {
|
|
197
|
-
this.scheduleHeartbeat(this);
|
|
198
|
-
}, this.getHeartbeatInterval());
|
|
199
|
-
break;
|
|
200
|
-
case models_1.MessageTag.LoginRequest:
|
|
201
|
-
this.log.debug(`Login request`, message);
|
|
202
|
-
break;
|
|
203
|
-
case models_1.MessageTag.IqStanza:
|
|
204
|
-
this.log.debug(`IqStanza: Not implemented`, message);
|
|
205
|
-
break;
|
|
206
|
-
default:
|
|
207
|
-
this.log.debug(`Unknown message`, message);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
this.streamId++;
|
|
211
|
-
}
|
|
212
|
-
handleHeartbeatPing(message) {
|
|
213
|
-
this.log.debug(`Heartbeat ping`, message);
|
|
214
|
-
let streamId = undefined;
|
|
215
|
-
let status = undefined;
|
|
216
|
-
if (this.newStreamIdAvailable()) {
|
|
217
|
-
streamId = this.getStreamId();
|
|
218
|
-
}
|
|
219
|
-
if (message.object && message.object.status) {
|
|
220
|
-
status = message.object.status;
|
|
221
|
-
}
|
|
222
|
-
if (this.client)
|
|
223
|
-
this.client.write(this.buildHeartbeatAckRequest(streamId, status));
|
|
224
|
-
}
|
|
225
|
-
handleHeartbeatAck(message) {
|
|
226
|
-
this.log.debug(`Heartbeat acknowledge`, message);
|
|
227
|
-
}
|
|
228
|
-
convertPayloadMessage(message) {
|
|
229
|
-
const { appData, ...otherData } = message.object;
|
|
230
|
-
const messageData = {};
|
|
231
|
-
appData.forEach((kv) => {
|
|
232
|
-
if (kv.key === "payload") {
|
|
233
|
-
const payload = (0, utils_1.parseJSON)(Buffer.from(kv.value, "base64").toString("utf8"), this.log);
|
|
234
|
-
messageData[kv.key] = payload;
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
messageData[kv.key] = kv.value;
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
return {
|
|
241
|
-
...otherData,
|
|
242
|
-
payload: messageData,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
getStreamId() {
|
|
246
|
-
this.lastStreamIdReported = this.streamId;
|
|
247
|
-
return this.streamId;
|
|
248
|
-
}
|
|
249
|
-
newStreamIdAvailable() {
|
|
250
|
-
return this.lastStreamIdReported != this.streamId;
|
|
251
|
-
}
|
|
252
|
-
scheduleHeartbeat(client) {
|
|
253
|
-
if (client.sendHeartbeat()) {
|
|
254
|
-
this.heartbeatTimeout = setTimeout(() => {
|
|
255
|
-
this.scheduleHeartbeat(client);
|
|
256
|
-
}, client.getHeartbeatInterval());
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
this.log.debug("Heartbeat disabled!");
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
sendHeartbeat() {
|
|
263
|
-
let streamId = undefined;
|
|
264
|
-
if (this.newStreamIdAvailable()) {
|
|
265
|
-
streamId = this.getStreamId();
|
|
266
|
-
}
|
|
267
|
-
if (this.client && this.isConnected()) {
|
|
268
|
-
this.log.debug(`Sending heartbeat...`, streamId);
|
|
269
|
-
this.client.write(this.buildHeartbeatPingRequest(streamId));
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
this.log.debug("No more connected, reconnect...");
|
|
274
|
-
this.scheduleReconnect();
|
|
275
|
-
}
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
isConnected() {
|
|
279
|
-
return this.loggedIn;
|
|
280
|
-
}
|
|
281
|
-
getHeartbeatInterval() {
|
|
282
|
-
return this.HEARTBEAT_INTERVAL;
|
|
283
|
-
}
|
|
284
|
-
getCurrentDelay() {
|
|
285
|
-
const delay = this.currentDelay == 0 ? 5000 : this.currentDelay;
|
|
286
|
-
if (this.currentDelay < 60000)
|
|
287
|
-
this.currentDelay += 10000;
|
|
288
|
-
if (this.currentDelay >= 60000 && this.currentDelay < 600000)
|
|
289
|
-
this.currentDelay += 60000;
|
|
290
|
-
return delay;
|
|
291
|
-
}
|
|
292
|
-
resetCurrentDelay() {
|
|
293
|
-
this.currentDelay = 0;
|
|
294
|
-
}
|
|
295
|
-
scheduleReconnect() {
|
|
296
|
-
const delay = this.getCurrentDelay();
|
|
297
|
-
this.log.debug("Schedule reconnect...", { delay: delay });
|
|
298
|
-
if (!this.reconnectTimeout)
|
|
299
|
-
this.reconnectTimeout = setTimeout(() => {
|
|
300
|
-
this.connect();
|
|
301
|
-
}, delay);
|
|
302
|
-
}
|
|
303
|
-
close() {
|
|
304
|
-
const wasConnected = this.isConnected();
|
|
305
|
-
this.initialize();
|
|
306
|
-
if (wasConnected)
|
|
307
|
-
this.emit("close");
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
PushClient
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.PushClient = void 0;
|
|
27
|
+
const Long = __importStar(require("long"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const protobuf_typescript_1 = require("protobuf-typescript");
|
|
30
|
+
const tls = __importStar(require("tls"));
|
|
31
|
+
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
|
|
32
|
+
const ts_log_1 = require("ts-log");
|
|
33
|
+
const models_1 = require("./models");
|
|
34
|
+
const parser_1 = require("./parser");
|
|
35
|
+
const utils_1 = require("../utils");
|
|
36
|
+
class PushClient extends tiny_typed_emitter_1.TypedEmitter {
|
|
37
|
+
constructor(pushClientParser, auth, log = ts_log_1.dummyLogger) {
|
|
38
|
+
super();
|
|
39
|
+
this.HOST = "mtalk.google.com";
|
|
40
|
+
this.PORT = 5228;
|
|
41
|
+
this.MCS_VERSION = 41;
|
|
42
|
+
this.HEARTBEAT_INTERVAL = 5 * 60 * 1000;
|
|
43
|
+
this.loggedIn = false;
|
|
44
|
+
this.streamId = 0;
|
|
45
|
+
this.lastStreamIdReported = -1;
|
|
46
|
+
this.currentDelay = 0;
|
|
47
|
+
this.persistentIds = [];
|
|
48
|
+
this.log = log;
|
|
49
|
+
this.pushClientParser = pushClientParser;
|
|
50
|
+
this.auth = auth;
|
|
51
|
+
}
|
|
52
|
+
static async init(auth, log = ts_log_1.dummyLogger) {
|
|
53
|
+
this.proto = await (0, protobuf_typescript_1.load)(path.join(__dirname, "./proto/mcs.proto"));
|
|
54
|
+
const pushClientParser = await parser_1.PushClientParser.init(log);
|
|
55
|
+
return new PushClient(pushClientParser, auth, log);
|
|
56
|
+
}
|
|
57
|
+
initialize() {
|
|
58
|
+
this.loggedIn = false;
|
|
59
|
+
this.streamId = 0;
|
|
60
|
+
this.lastStreamIdReported = -1;
|
|
61
|
+
if (this.client) {
|
|
62
|
+
this.client.removeAllListeners();
|
|
63
|
+
this.client.destroy();
|
|
64
|
+
this.client = undefined;
|
|
65
|
+
}
|
|
66
|
+
this.pushClientParser.resetState();
|
|
67
|
+
if (this.reconnectTimeout) {
|
|
68
|
+
clearTimeout(this.reconnectTimeout);
|
|
69
|
+
this.reconnectTimeout = undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
getPersistentIds() {
|
|
73
|
+
return this.persistentIds;
|
|
74
|
+
}
|
|
75
|
+
setPersistentIds(ids) {
|
|
76
|
+
this.persistentIds = ids;
|
|
77
|
+
}
|
|
78
|
+
connect() {
|
|
79
|
+
this.initialize();
|
|
80
|
+
this.pushClientParser.on("message", (message) => this.handleParsedMessage(message));
|
|
81
|
+
this.client = tls.connect(this.PORT, this.HOST, {
|
|
82
|
+
rejectUnauthorized: false,
|
|
83
|
+
});
|
|
84
|
+
this.client.setKeepAlive(true);
|
|
85
|
+
// For debugging purposes
|
|
86
|
+
//this.client.enableTrace();
|
|
87
|
+
this.client.on("connect", () => this.onSocketConnect());
|
|
88
|
+
this.client.on("close", () => this.onSocketClose());
|
|
89
|
+
this.client.on("error", (error) => this.onSocketError(error));
|
|
90
|
+
this.client.on("data", (newData) => this.onSocketData(newData));
|
|
91
|
+
this.client.write(this.buildLoginRequest());
|
|
92
|
+
}
|
|
93
|
+
buildLoginRequest() {
|
|
94
|
+
const androidId = this.auth.androidId;
|
|
95
|
+
const securityToken = this.auth.securityToken;
|
|
96
|
+
const LoginRequestType = PushClient.proto.lookupType("mcs_proto.LoginRequest");
|
|
97
|
+
const hexAndroidId = Long.fromString(androidId).toString(16);
|
|
98
|
+
const loginRequest = {
|
|
99
|
+
adaptiveHeartbeat: false,
|
|
100
|
+
authService: 2,
|
|
101
|
+
authToken: securityToken,
|
|
102
|
+
id: "chrome-63.0.3234.0",
|
|
103
|
+
domain: "mcs.android.com",
|
|
104
|
+
deviceId: `android-${hexAndroidId}`,
|
|
105
|
+
networkType: 1,
|
|
106
|
+
resource: androidId,
|
|
107
|
+
user: androidId,
|
|
108
|
+
useRmq2: true,
|
|
109
|
+
setting: [{ name: "new_vc", value: "1" }],
|
|
110
|
+
clientEvent: [],
|
|
111
|
+
receivedPersistentId: this.persistentIds,
|
|
112
|
+
};
|
|
113
|
+
const errorMessage = LoginRequestType.verify(loginRequest);
|
|
114
|
+
if (errorMessage) {
|
|
115
|
+
throw new Error(errorMessage);
|
|
116
|
+
}
|
|
117
|
+
const buffer = LoginRequestType.encodeDelimited(loginRequest).finish();
|
|
118
|
+
return Buffer.concat([Buffer.from([this.MCS_VERSION, models_1.MessageTag.LoginRequest]), buffer]);
|
|
119
|
+
}
|
|
120
|
+
buildHeartbeatPingRequest(stream_id) {
|
|
121
|
+
const heartbeatPingRequest = {};
|
|
122
|
+
if (stream_id) {
|
|
123
|
+
heartbeatPingRequest.last_stream_id_received = stream_id;
|
|
124
|
+
}
|
|
125
|
+
this.log.debug(`heartbeatPingRequest`, heartbeatPingRequest);
|
|
126
|
+
const HeartbeatPingRequestType = PushClient.proto.lookupType("mcs_proto.HeartbeatPing");
|
|
127
|
+
const errorMessage = HeartbeatPingRequestType.verify(heartbeatPingRequest);
|
|
128
|
+
if (errorMessage) {
|
|
129
|
+
throw new Error(errorMessage);
|
|
130
|
+
}
|
|
131
|
+
const buffer = HeartbeatPingRequestType.encodeDelimited(heartbeatPingRequest).finish();
|
|
132
|
+
return Buffer.concat([Buffer.from([models_1.MessageTag.HeartbeatPing]), buffer]);
|
|
133
|
+
}
|
|
134
|
+
buildHeartbeatAckRequest(stream_id, status) {
|
|
135
|
+
const heartbeatAckRequest = {};
|
|
136
|
+
if (stream_id && !status) {
|
|
137
|
+
heartbeatAckRequest.last_stream_id_received = stream_id;
|
|
138
|
+
}
|
|
139
|
+
else if (!stream_id && status) {
|
|
140
|
+
heartbeatAckRequest.status = status;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
heartbeatAckRequest.last_stream_id_received = stream_id;
|
|
144
|
+
heartbeatAckRequest.status = status;
|
|
145
|
+
}
|
|
146
|
+
this.log.debug(`heartbeatAckRequest`, heartbeatAckRequest);
|
|
147
|
+
const HeartbeatAckRequestType = PushClient.proto.lookupType("mcs_proto.HeartbeatAck");
|
|
148
|
+
const errorMessage = HeartbeatAckRequestType.verify(heartbeatAckRequest);
|
|
149
|
+
if (errorMessage) {
|
|
150
|
+
throw new Error(errorMessage);
|
|
151
|
+
}
|
|
152
|
+
const buffer = HeartbeatAckRequestType.encodeDelimited(heartbeatAckRequest).finish();
|
|
153
|
+
return Buffer.concat([Buffer.from([models_1.MessageTag.HeartbeatAck]), buffer]);
|
|
154
|
+
}
|
|
155
|
+
onSocketData(newData) {
|
|
156
|
+
this.pushClientParser.handleData(newData);
|
|
157
|
+
}
|
|
158
|
+
onSocketConnect() {
|
|
159
|
+
//
|
|
160
|
+
}
|
|
161
|
+
onSocketClose() {
|
|
162
|
+
this.loggedIn = false;
|
|
163
|
+
if (this.heartbeatTimeout) {
|
|
164
|
+
clearTimeout(this.heartbeatTimeout);
|
|
165
|
+
this.heartbeatTimeout = undefined;
|
|
166
|
+
}
|
|
167
|
+
this.emit("close");
|
|
168
|
+
this.scheduleReconnect();
|
|
169
|
+
}
|
|
170
|
+
onSocketError(error) {
|
|
171
|
+
this.log.error(`onSocketError:`, error);
|
|
172
|
+
}
|
|
173
|
+
handleParsedMessage(message) {
|
|
174
|
+
this.resetCurrentDelay();
|
|
175
|
+
switch (message.tag) {
|
|
176
|
+
case models_1.MessageTag.DataMessageStanza:
|
|
177
|
+
this.log.debug(`DataMessageStanza`, message);
|
|
178
|
+
if (message.object && message.object.persistentId)
|
|
179
|
+
this.persistentIds.push(message.object.persistentId);
|
|
180
|
+
this.emit("message", this.convertPayloadMessage(message));
|
|
181
|
+
break;
|
|
182
|
+
case models_1.MessageTag.HeartbeatPing:
|
|
183
|
+
this.handleHeartbeatPing(message);
|
|
184
|
+
break;
|
|
185
|
+
case models_1.MessageTag.HeartbeatAck:
|
|
186
|
+
this.handleHeartbeatAck(message);
|
|
187
|
+
break;
|
|
188
|
+
case models_1.MessageTag.Close:
|
|
189
|
+
this.log.debug(`Close: Server requested close`, message);
|
|
190
|
+
break;
|
|
191
|
+
case models_1.MessageTag.LoginResponse:
|
|
192
|
+
this.log.debug("Login response: GCM -> logged in -> waiting for push messages...");
|
|
193
|
+
this.loggedIn = true;
|
|
194
|
+
this.persistentIds = [];
|
|
195
|
+
this.emit("connect");
|
|
196
|
+
this.heartbeatTimeout = setTimeout(() => {
|
|
197
|
+
this.scheduleHeartbeat(this);
|
|
198
|
+
}, this.getHeartbeatInterval());
|
|
199
|
+
break;
|
|
200
|
+
case models_1.MessageTag.LoginRequest:
|
|
201
|
+
this.log.debug(`Login request`, message);
|
|
202
|
+
break;
|
|
203
|
+
case models_1.MessageTag.IqStanza:
|
|
204
|
+
this.log.debug(`IqStanza: Not implemented`, message);
|
|
205
|
+
break;
|
|
206
|
+
default:
|
|
207
|
+
this.log.debug(`Unknown message`, message);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.streamId++;
|
|
211
|
+
}
|
|
212
|
+
handleHeartbeatPing(message) {
|
|
213
|
+
this.log.debug(`Heartbeat ping`, message);
|
|
214
|
+
let streamId = undefined;
|
|
215
|
+
let status = undefined;
|
|
216
|
+
if (this.newStreamIdAvailable()) {
|
|
217
|
+
streamId = this.getStreamId();
|
|
218
|
+
}
|
|
219
|
+
if (message.object && message.object.status) {
|
|
220
|
+
status = message.object.status;
|
|
221
|
+
}
|
|
222
|
+
if (this.client)
|
|
223
|
+
this.client.write(this.buildHeartbeatAckRequest(streamId, status));
|
|
224
|
+
}
|
|
225
|
+
handleHeartbeatAck(message) {
|
|
226
|
+
this.log.debug(`Heartbeat acknowledge`, message);
|
|
227
|
+
}
|
|
228
|
+
convertPayloadMessage(message) {
|
|
229
|
+
const { appData, ...otherData } = message.object;
|
|
230
|
+
const messageData = {};
|
|
231
|
+
appData.forEach((kv) => {
|
|
232
|
+
if (kv.key === "payload") {
|
|
233
|
+
const payload = (0, utils_1.parseJSON)(Buffer.from(kv.value, "base64").toString("utf8"), this.log);
|
|
234
|
+
messageData[kv.key] = payload;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
messageData[kv.key] = kv.value;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
return {
|
|
241
|
+
...otherData,
|
|
242
|
+
payload: messageData,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
getStreamId() {
|
|
246
|
+
this.lastStreamIdReported = this.streamId;
|
|
247
|
+
return this.streamId;
|
|
248
|
+
}
|
|
249
|
+
newStreamIdAvailable() {
|
|
250
|
+
return this.lastStreamIdReported != this.streamId;
|
|
251
|
+
}
|
|
252
|
+
scheduleHeartbeat(client) {
|
|
253
|
+
if (client.sendHeartbeat()) {
|
|
254
|
+
this.heartbeatTimeout = setTimeout(() => {
|
|
255
|
+
this.scheduleHeartbeat(client);
|
|
256
|
+
}, client.getHeartbeatInterval());
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
this.log.debug("Heartbeat disabled!");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
sendHeartbeat() {
|
|
263
|
+
let streamId = undefined;
|
|
264
|
+
if (this.newStreamIdAvailable()) {
|
|
265
|
+
streamId = this.getStreamId();
|
|
266
|
+
}
|
|
267
|
+
if (this.client && this.isConnected()) {
|
|
268
|
+
this.log.debug(`Sending heartbeat...`, streamId);
|
|
269
|
+
this.client.write(this.buildHeartbeatPingRequest(streamId));
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.log.debug("No more connected, reconnect...");
|
|
274
|
+
this.scheduleReconnect();
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
isConnected() {
|
|
279
|
+
return this.loggedIn;
|
|
280
|
+
}
|
|
281
|
+
getHeartbeatInterval() {
|
|
282
|
+
return this.HEARTBEAT_INTERVAL;
|
|
283
|
+
}
|
|
284
|
+
getCurrentDelay() {
|
|
285
|
+
const delay = this.currentDelay == 0 ? 5000 : this.currentDelay;
|
|
286
|
+
if (this.currentDelay < 60000)
|
|
287
|
+
this.currentDelay += 10000;
|
|
288
|
+
if (this.currentDelay >= 60000 && this.currentDelay < 600000)
|
|
289
|
+
this.currentDelay += 60000;
|
|
290
|
+
return delay;
|
|
291
|
+
}
|
|
292
|
+
resetCurrentDelay() {
|
|
293
|
+
this.currentDelay = 0;
|
|
294
|
+
}
|
|
295
|
+
scheduleReconnect() {
|
|
296
|
+
const delay = this.getCurrentDelay();
|
|
297
|
+
this.log.debug("Schedule reconnect...", { delay: delay });
|
|
298
|
+
if (!this.reconnectTimeout)
|
|
299
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
300
|
+
this.connect();
|
|
301
|
+
}, delay);
|
|
302
|
+
}
|
|
303
|
+
close() {
|
|
304
|
+
const wasConnected = this.isConnected();
|
|
305
|
+
this.initialize();
|
|
306
|
+
if (wasConnected)
|
|
307
|
+
this.emit("close");
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
PushClient.proto = null;
|
|
311
|
+
exports.PushClient = PushClient;
|
|
312
312
|
//# sourceMappingURL=client.js.map
|