@vex-chat/libvex 1.0.1 → 1.1.0
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/dist/Client.d.ts +25 -15
- package/dist/Client.js +210 -159
- package/dist/Client.js.map +1 -1
- package/dist/IStorage.d.ts +1 -1
- package/dist/IStorage.js +1 -1
- package/dist/IStorage.js.map +1 -1
- package/dist/__tests__/harness/memory-storage.d.ts +45 -0
- package/dist/__tests__/harness/memory-storage.js +181 -0
- package/dist/__tests__/harness/memory-storage.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/keystore/memory.d.ts +11 -0
- package/dist/keystore/memory.js +18 -0
- package/dist/keystore/memory.js.map +1 -0
- package/dist/keystore/node.d.ts +10 -0
- package/dist/keystore/node.js +64 -0
- package/dist/keystore/node.js.map +1 -0
- package/dist/keystore/types.d.ts +4 -0
- package/dist/keystore/types.js +2 -0
- package/dist/keystore/types.js.map +1 -0
- package/dist/preset/expo.d.ts +2 -0
- package/dist/preset/expo.js +39 -0
- package/dist/preset/expo.js.map +1 -0
- package/dist/preset/node.d.ts +12 -0
- package/dist/preset/node.js +17 -0
- package/dist/preset/node.js.map +1 -0
- package/dist/preset/tauri.d.ts +2 -0
- package/dist/preset/tauri.js +36 -0
- package/dist/preset/tauri.js.map +1 -0
- package/dist/preset/test.d.ts +10 -0
- package/dist/preset/test.js +29 -0
- package/dist/preset/test.js.map +1 -0
- package/dist/preset/types.d.ts +14 -0
- package/dist/preset/types.js +2 -0
- package/dist/preset/types.js.map +1 -0
- package/dist/storage/expo.d.ts +3 -0
- package/dist/storage/expo.js +18 -0
- package/dist/storage/expo.js.map +1 -0
- package/dist/storage/node.d.ts +3 -0
- package/dist/storage/node.js +24 -0
- package/dist/storage/node.js.map +1 -0
- package/dist/storage/schema.d.ts +76 -0
- package/dist/storage/schema.js +2 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/{Storage.d.ts → storage/sqlite.d.ts} +19 -21
- package/dist/storage/sqlite.js +488 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/storage/tauri.d.ts +3 -0
- package/dist/storage/tauri.js +21 -0
- package/dist/storage/tauri.js.map +1 -0
- package/dist/transport/browser.d.ts +17 -0
- package/dist/transport/browser.js +56 -0
- package/dist/transport/browser.js.map +1 -0
- package/dist/transport/types.d.ts +20 -0
- package/dist/transport/types.js +2 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/utils/createLogger.js +1 -8
- package/dist/utils/createLogger.js.map +1 -1
- package/package.json +100 -16
- package/dist/Storage.js +0 -444
- package/dist/Storage.js.map +0 -1
package/dist/Client.js
CHANGED
|
@@ -2,32 +2,31 @@
|
|
|
2
2
|
import { sleep } from "@extrahash/sleep";
|
|
3
3
|
import { xConcat, xConstants, xDH, xEncode, xHMAC, xKDF, XKeyConvert, xMakeNonce, xMnemonic, XUtils, } from "@vex-chat/crypto";
|
|
4
4
|
import { MailType } from "@vex-chat/types";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import chalk from "chalk";
|
|
9
|
-
import { EventEmitter } from "events";
|
|
5
|
+
import axios, { AxiosError } from "axios";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import { EventEmitter } from "eventemitter3";
|
|
10
8
|
import { Packr } from "msgpackr";
|
|
11
9
|
// useRecords:false emits standard msgpack (no nonstandard record extension).
|
|
12
10
|
// moreTypes:false keeps the extension set to what every other decoder understands.
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
11
|
+
const _packr = new Packr({ useRecords: false, moreTypes: false });
|
|
12
|
+
// Packr.encode() returns a subarray of its internal pool buffer.
|
|
13
|
+
// In browsers, XMLHttpRequest.send() sends the full underlying ArrayBuffer,
|
|
14
|
+
// not just the slice — corrupting the payload (axios issue #4068).
|
|
15
|
+
// Wrap encode to always return a fresh copy.
|
|
16
|
+
const msgpack = {
|
|
17
|
+
encode: (value) => {
|
|
18
|
+
const packed = _packr.encode(value);
|
|
19
|
+
return new Uint8Array(packed.buffer.slice(packed.byteOffset, packed.byteOffset + packed.byteLength));
|
|
20
|
+
},
|
|
21
|
+
decode: _packr.decode.bind(_packr),
|
|
22
|
+
};
|
|
16
23
|
import objectHash from "object-hash";
|
|
17
|
-
import * as os from "node:os";
|
|
18
|
-
import { performance } from "node:perf_hooks";
|
|
19
24
|
import nacl from "tweetnacl";
|
|
20
25
|
import * as uuid from "uuid";
|
|
21
|
-
import winston from "winston";
|
|
22
|
-
import WebSocket from "ws";
|
|
23
|
-
import { Storage } from "./Storage.js";
|
|
24
26
|
import { capitalize } from "./utils/capitalize.js";
|
|
25
|
-
import { createLogger } from "./utils/createLogger.js";
|
|
26
27
|
import { formatBytes } from "./utils/formatBytes.js";
|
|
27
28
|
import { sqlSessionToCrypto } from "./utils/sqlSessionToCrypto.js";
|
|
28
29
|
import { uuidToUint8 } from "./utils/uint8uuid.js";
|
|
29
|
-
ax.defaults.withCredentials = true;
|
|
30
|
-
ax.defaults.responseType = "arraybuffer";
|
|
31
30
|
const protocolMsgRegex = /��\w+:\w+��/g;
|
|
32
31
|
/**
|
|
33
32
|
* Client provides an interface for you to use a vex chat server and
|
|
@@ -78,17 +77,17 @@ const protocolMsgRegex = /��\w+:\w+��/g;
|
|
|
78
77
|
*/
|
|
79
78
|
export class Client extends EventEmitter {
|
|
80
79
|
/**
|
|
81
|
-
*
|
|
80
|
+
* Encrypts a secret key with a password.
|
|
82
81
|
*
|
|
83
82
|
* Pass-through utility from `@vex-chat/crypto`.
|
|
84
83
|
*/
|
|
85
|
-
static
|
|
84
|
+
static encryptKeyData = XUtils.encryptKeyData;
|
|
86
85
|
/**
|
|
87
|
-
*
|
|
86
|
+
* Decrypts a secret key from encrypted data produced by encryptKeyData().
|
|
88
87
|
*
|
|
89
88
|
* Pass-through utility from `@vex-chat/crypto`.
|
|
90
89
|
*/
|
|
91
|
-
static
|
|
90
|
+
static decryptKeyData = XUtils.decryptKeyData;
|
|
92
91
|
/**
|
|
93
92
|
* Creates and initializes a client in one step.
|
|
94
93
|
*
|
|
@@ -102,7 +101,36 @@ export class Client extends EventEmitter {
|
|
|
102
101
|
* ```
|
|
103
102
|
*/
|
|
104
103
|
static create = async (privateKey, options, storage) => {
|
|
105
|
-
|
|
104
|
+
let opts = options;
|
|
105
|
+
if (!opts?.adapters) {
|
|
106
|
+
const { default: WebSocket } = await import("ws");
|
|
107
|
+
const { createLogger: makeLog } = await import("./utils/createLogger.js");
|
|
108
|
+
opts = {
|
|
109
|
+
...opts,
|
|
110
|
+
adapters: {
|
|
111
|
+
logger: makeLog("libvex", opts?.logLevel),
|
|
112
|
+
WebSocket: WebSocket,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Lazily create Node Storage only on the Node path (no adapters).
|
|
117
|
+
// When adapters are provided (browser/RN), the caller must supply storage
|
|
118
|
+
// via PlatformPreset.createStorage() — there is no Node fallback.
|
|
119
|
+
let resolvedStorage = storage;
|
|
120
|
+
if (!resolvedStorage) {
|
|
121
|
+
if (opts?.adapters) {
|
|
122
|
+
throw new Error("No storage provided. When using adapters (browser/RN), pass storage from your PlatformPreset.");
|
|
123
|
+
}
|
|
124
|
+
const { createNodeStorage } = await import("./storage/node.js");
|
|
125
|
+
const dbFileName = opts?.inMemoryDb
|
|
126
|
+
? ":memory:"
|
|
127
|
+
: XUtils.encodeHex(nacl.sign.keyPair.fromSecretKey(XUtils.decodeHex(privateKey || "")).publicKey) + ".sqlite";
|
|
128
|
+
const dbPath = opts?.dbFolder
|
|
129
|
+
? opts.dbFolder + "/" + dbFileName
|
|
130
|
+
: dbFileName;
|
|
131
|
+
resolvedStorage = createNodeStorage(dbPath, privateKey || XUtils.encodeHex(nacl.sign.keyPair().secretKey));
|
|
132
|
+
}
|
|
133
|
+
const client = new Client(privateKey, opts, resolvedStorage);
|
|
106
134
|
await client.init();
|
|
107
135
|
return client;
|
|
108
136
|
};
|
|
@@ -397,6 +425,7 @@ export class Client extends EventEmitter {
|
|
|
397
425
|
dbPath;
|
|
398
426
|
conn;
|
|
399
427
|
host;
|
|
428
|
+
adapters;
|
|
400
429
|
firstMailFetch = true;
|
|
401
430
|
// these are created from one set of sign keys
|
|
402
431
|
signKeys;
|
|
@@ -410,7 +439,7 @@ export class Client extends EventEmitter {
|
|
|
410
439
|
isAlive = true;
|
|
411
440
|
reading = false;
|
|
412
441
|
fetchingMail = false;
|
|
413
|
-
|
|
442
|
+
ax;
|
|
414
443
|
log;
|
|
415
444
|
pingInterval = null;
|
|
416
445
|
mailInterval;
|
|
@@ -418,9 +447,16 @@ export class Client extends EventEmitter {
|
|
|
418
447
|
token = null;
|
|
419
448
|
forwarded = [];
|
|
420
449
|
prefixes;
|
|
450
|
+
options;
|
|
421
451
|
constructor(privateKey, options, storage) {
|
|
422
452
|
super();
|
|
423
|
-
this.
|
|
453
|
+
this.options = options;
|
|
454
|
+
this.log = options?.adapters?.logger ?? {
|
|
455
|
+
info() { },
|
|
456
|
+
warn() { },
|
|
457
|
+
error() { },
|
|
458
|
+
debug() { },
|
|
459
|
+
};
|
|
424
460
|
this.prefixes = options?.unsafeHttp
|
|
425
461
|
? { HTTP: "http://", WS: "ws://" }
|
|
426
462
|
: { HTTP: "https://", WS: "wss://" };
|
|
@@ -438,17 +474,21 @@ export class Client extends EventEmitter {
|
|
|
438
474
|
this.dbPath = options?.dbFolder
|
|
439
475
|
? options?.dbFolder + "/" + dbFileName
|
|
440
476
|
: dbFileName;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
477
|
+
if (!storage) {
|
|
478
|
+
throw new Error("No storage provided. Use Client.create() which resolves storage automatically.");
|
|
479
|
+
}
|
|
480
|
+
this.database = storage;
|
|
444
481
|
this.database.on("error", (error) => {
|
|
445
482
|
this.log.error(error.toString());
|
|
446
483
|
this.close(true);
|
|
447
484
|
});
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
485
|
+
if (!options?.adapters) {
|
|
486
|
+
throw new Error("No adapters provided. Use Client.create() which resolves adapters automatically.");
|
|
487
|
+
}
|
|
488
|
+
this.adapters = options.adapters;
|
|
489
|
+
this.ax = axios.create({ responseType: "arraybuffer" });
|
|
490
|
+
// Placeholder connection — replaced by initSocket() during connect()
|
|
491
|
+
this.conn = new this.adapters.WebSocket("ws://localhost:1234");
|
|
452
492
|
this.conn.onerror = () => { };
|
|
453
493
|
this.log.info("Client debug information: " +
|
|
454
494
|
JSON.stringify({
|
|
@@ -456,8 +496,7 @@ export class Client extends EventEmitter {
|
|
|
456
496
|
host: this.getHost(),
|
|
457
497
|
dbPath: this.dbPath,
|
|
458
498
|
environment: {
|
|
459
|
-
|
|
460
|
-
isNode,
|
|
499
|
+
platform: this.options?.deviceName ?? "unknown",
|
|
461
500
|
},
|
|
462
501
|
options,
|
|
463
502
|
}, null, 4));
|
|
@@ -503,7 +542,7 @@ export class Client extends EventEmitter {
|
|
|
503
542
|
};
|
|
504
543
|
}
|
|
505
544
|
/**
|
|
506
|
-
* Authenticates with username/password and stores the auth token
|
|
545
|
+
* Authenticates with username/password and stores the Bearer auth token.
|
|
507
546
|
*
|
|
508
547
|
* @param username Account username.
|
|
509
548
|
* @param password Account password.
|
|
@@ -517,23 +556,16 @@ export class Client extends EventEmitter {
|
|
|
517
556
|
*/
|
|
518
557
|
async login(username, password) {
|
|
519
558
|
try {
|
|
520
|
-
const res = await ax.post(this.getHost() + "/auth", msgpack.encode({
|
|
559
|
+
const res = await this.ax.post(this.getHost() + "/auth", msgpack.encode({
|
|
521
560
|
username,
|
|
522
561
|
password,
|
|
523
562
|
}), {
|
|
524
563
|
headers: { "Content-Type": "application/msgpack" },
|
|
525
564
|
});
|
|
526
|
-
const { user, token } = msgpack.decode(
|
|
527
|
-
const cookies = res.headers["set-cookie"];
|
|
528
|
-
if (cookies) {
|
|
529
|
-
for (const cookie of cookies) {
|
|
530
|
-
if (cookie.includes("auth")) {
|
|
531
|
-
this.addCookie(cookie);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
565
|
+
const { user, token } = msgpack.decode(new Uint8Array(res.data));
|
|
535
566
|
this.setUser(user);
|
|
536
567
|
this.token = token;
|
|
568
|
+
this.ax.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
537
569
|
}
|
|
538
570
|
catch (err) {
|
|
539
571
|
console.error(err.toString());
|
|
@@ -542,9 +574,38 @@ export class Client extends EventEmitter {
|
|
|
542
574
|
return null;
|
|
543
575
|
}
|
|
544
576
|
/**
|
|
545
|
-
*
|
|
546
|
-
*
|
|
577
|
+
* Authenticates using the device's Ed25519 signing key.
|
|
578
|
+
* No password needed — proves possession of the private key via
|
|
579
|
+
* challenge-response. Issues a short-lived (1-hour) JWT.
|
|
580
|
+
*
|
|
581
|
+
* Used by auto-login when stored credentials have a deviceKey
|
|
582
|
+
* but no valid session.
|
|
547
583
|
*/
|
|
584
|
+
async loginWithDeviceKey(deviceID) {
|
|
585
|
+
try {
|
|
586
|
+
const id = deviceID ?? this.device?.deviceID;
|
|
587
|
+
if (!id) {
|
|
588
|
+
return new Error("No deviceID — pass it or connect first.");
|
|
589
|
+
}
|
|
590
|
+
const signKeyHex = XUtils.encodeHex(this.signKeys.publicKey);
|
|
591
|
+
const challengeRes = await this.ax.post(this.getHost() + "/auth/device", msgpack.encode({
|
|
592
|
+
deviceID: id,
|
|
593
|
+
signKey: signKeyHex,
|
|
594
|
+
}), { headers: { "Content-Type": "application/msgpack" } });
|
|
595
|
+
const { challengeID, challenge } = msgpack.decode(new Uint8Array(challengeRes.data));
|
|
596
|
+
const signed = XUtils.encodeHex(nacl.sign(XUtils.decodeHex(challenge), this.signKeys.secretKey));
|
|
597
|
+
const verifyRes = await this.ax.post(this.getHost() + "/auth/device/verify", msgpack.encode({ challengeID, signed }), { headers: { "Content-Type": "application/msgpack" } });
|
|
598
|
+
const { user, token } = msgpack.decode(new Uint8Array(verifyRes.data));
|
|
599
|
+
this.setUser(user);
|
|
600
|
+
this.token = token;
|
|
601
|
+
this.ax.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
602
|
+
}
|
|
603
|
+
catch (err) {
|
|
604
|
+
this.log.error("Device-key auth failed: " + err);
|
|
605
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
606
|
+
}
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
548
609
|
/**
|
|
549
610
|
* Returns details about the currently authenticated session.
|
|
550
611
|
*
|
|
@@ -557,28 +618,26 @@ export class Client extends EventEmitter {
|
|
|
557
618
|
* ```
|
|
558
619
|
*/
|
|
559
620
|
async whoami() {
|
|
560
|
-
const res = await ax.post(this.getHost() + "/whoami"
|
|
561
|
-
|
|
562
|
-
responseType: "arraybuffer",
|
|
563
|
-
});
|
|
564
|
-
const whoami = msgpack.decode(Buffer.from(res.data));
|
|
621
|
+
const res = await this.ax.post(this.getHost() + "/whoami");
|
|
622
|
+
const whoami = msgpack.decode(new Uint8Array(res.data));
|
|
565
623
|
return whoami;
|
|
566
624
|
}
|
|
567
625
|
/**
|
|
568
626
|
* Logs out the current authenticated session from the server.
|
|
569
627
|
*/
|
|
570
628
|
async logout() {
|
|
571
|
-
await ax.post(this.getHost() + "/goodbye");
|
|
629
|
+
await this.ax.post(this.getHost() + "/goodbye");
|
|
572
630
|
}
|
|
573
631
|
/**
|
|
574
|
-
* Connects your device to the chat. You must have
|
|
632
|
+
* Connects your device to the chat. You must have a valid Bearer token.
|
|
575
633
|
* You can check whoami() to see before calling connect().
|
|
576
634
|
*/
|
|
577
635
|
async connect() {
|
|
578
636
|
const { user, token } = await this.whoami();
|
|
579
637
|
this.token = token;
|
|
638
|
+
this.ax.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
580
639
|
if (!user || !token) {
|
|
581
|
-
throw new Error("Auth
|
|
640
|
+
throw new Error("Auth token missing or expired. Log in again.");
|
|
582
641
|
}
|
|
583
642
|
this.setUser(user);
|
|
584
643
|
this.device = await this.retrieveOrCreateDevice();
|
|
@@ -587,17 +646,14 @@ export class Client extends EventEmitter {
|
|
|
587
646
|
throw new Error("Couldn't get connect token.");
|
|
588
647
|
}
|
|
589
648
|
const signed = nacl.sign(Uint8Array.from(uuid.parse(connectToken.key)), this.signKeys.secretKey);
|
|
590
|
-
const res = await ax.post(this.getHost() + "/device/" + this.device.deviceID + "/connect", msgpack.encode({ signed }), { headers: { "Content-Type": "application/msgpack" } });
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
for (const cookie of cookies) {
|
|
594
|
-
if (cookie.includes("device")) {
|
|
595
|
-
this.addCookie(cookie);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
649
|
+
const res = await this.ax.post(this.getHost() + "/device/" + this.device.deviceID + "/connect", msgpack.encode({ signed }), { headers: { "Content-Type": "application/msgpack" } });
|
|
650
|
+
const { deviceToken } = msgpack.decode(new Uint8Array(res.data));
|
|
651
|
+
this.ax.defaults.headers.common["X-Device-Token"] = deviceToken;
|
|
599
652
|
this.log.info("Starting websocket.");
|
|
600
653
|
this.initSocket();
|
|
654
|
+
// Yield the event loop so the WS open callback fires and sends the
|
|
655
|
+
// auth message before OTK generation blocks for ~5s on mobile.
|
|
656
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
601
657
|
await this.negotiateOTK();
|
|
602
658
|
}
|
|
603
659
|
/**
|
|
@@ -624,11 +680,11 @@ export class Client extends EventEmitter {
|
|
|
624
680
|
preKeySignature: XUtils.encodeHex(this.xKeyRing.preKeys.signature),
|
|
625
681
|
preKeyIndex: this.xKeyRing.preKeys.index,
|
|
626
682
|
password,
|
|
627
|
-
deviceName:
|
|
683
|
+
deviceName: this.options?.deviceName ?? "unknown",
|
|
628
684
|
};
|
|
629
685
|
try {
|
|
630
|
-
const res = await ax.post(this.getHost() + "/register", msgpack.encode(regMsg), { headers: { "Content-Type": "application/msgpack" } });
|
|
631
|
-
this.setUser(msgpack.decode(
|
|
686
|
+
const res = await this.ax.post(this.getHost() + "/register", msgpack.encode(regMsg), { headers: { "Content-Type": "application/msgpack" } });
|
|
687
|
+
this.setUser(msgpack.decode(new Uint8Array(res.data)));
|
|
632
688
|
return [this.getUser(), null];
|
|
633
689
|
}
|
|
634
690
|
catch (err) {
|
|
@@ -651,32 +707,31 @@ export class Client extends EventEmitter {
|
|
|
651
707
|
return this.user?.username + "<" + this.device?.deviceID + ">";
|
|
652
708
|
}
|
|
653
709
|
async redeemInvite(inviteID) {
|
|
654
|
-
const res = await ax.patch(this.getHost() + "/invite/" + inviteID);
|
|
655
|
-
return msgpack.decode(
|
|
710
|
+
const res = await this.ax.patch(this.getHost() + "/invite/" + inviteID);
|
|
711
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
656
712
|
}
|
|
657
713
|
async retrieveInvites(serverID) {
|
|
658
|
-
const res = await ax.get(this.getHost() + "/server/" + serverID + "/invites");
|
|
659
|
-
return msgpack.decode(
|
|
714
|
+
const res = await this.ax.get(this.getHost() + "/server/" + serverID + "/invites");
|
|
715
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
660
716
|
}
|
|
661
717
|
async createInvite(serverID, duration) {
|
|
662
718
|
const payload = {
|
|
663
719
|
serverID,
|
|
664
720
|
duration,
|
|
665
721
|
};
|
|
666
|
-
const res = await ax.post(this.getHost() + "/server/" + serverID + "/invites", payload);
|
|
667
|
-
return msgpack.decode(
|
|
722
|
+
const res = await this.ax.post(this.getHost() + "/server/" + serverID + "/invites", payload);
|
|
723
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
668
724
|
}
|
|
669
725
|
async retrieveEmojiList(serverID) {
|
|
670
|
-
const res = await ax.get(this.getHost() + "/server/" + serverID + "/emoji");
|
|
671
|
-
return msgpack.decode(
|
|
726
|
+
const res = await this.ax.get(this.getHost() + "/server/" + serverID + "/emoji");
|
|
727
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
672
728
|
}
|
|
673
729
|
async retrieveEmojiByID(emojiID) {
|
|
674
|
-
const res = await ax.get(this.getHost() + "/emoji/" + emojiID + "/details");
|
|
675
|
-
// this is actually empty string
|
|
730
|
+
const res = await this.ax.get(this.getHost() + "/emoji/" + emojiID + "/details");
|
|
676
731
|
if (!res.data) {
|
|
677
732
|
return null;
|
|
678
733
|
}
|
|
679
|
-
return msgpack.decode(
|
|
734
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
680
735
|
}
|
|
681
736
|
async leaveServer(serverID) {
|
|
682
737
|
const permissionList = await this.permissions.retrieve();
|
|
@@ -696,25 +751,13 @@ export class Client extends EventEmitter {
|
|
|
696
751
|
}
|
|
697
752
|
throw new Error("Couldn't kick user.");
|
|
698
753
|
}
|
|
699
|
-
addCookie(cookie) {
|
|
700
|
-
if (!this.cookies.includes(cookie)) {
|
|
701
|
-
this.cookies.push(cookie);
|
|
702
|
-
this.log.info("cookies changed", this.getCookies());
|
|
703
|
-
if (isNode) {
|
|
704
|
-
ax.defaults.headers.cookie = this.cookies.join(";");
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
getCookies() {
|
|
709
|
-
return this.cookies.join(";");
|
|
710
|
-
}
|
|
711
754
|
async uploadEmoji(emoji, name, serverID) {
|
|
712
755
|
if (typeof FormData !== "undefined") {
|
|
713
756
|
const fpayload = new FormData();
|
|
714
757
|
fpayload.set("emoji", new Blob([new Uint8Array(emoji)]));
|
|
715
758
|
fpayload.set("name", name);
|
|
716
759
|
try {
|
|
717
|
-
const res = await ax.post(this.getHost() + "/emoji/" + serverID, fpayload, {
|
|
760
|
+
const res = await this.ax.post(this.getHost() + "/emoji/" + serverID, fpayload, {
|
|
718
761
|
headers: { "Content-Type": "multipart/form-data" },
|
|
719
762
|
onUploadProgress: (progressEvent) => {
|
|
720
763
|
const percentCompleted = Math.round((progressEvent.loaded * 100) /
|
|
@@ -730,7 +773,7 @@ export class Client extends EventEmitter {
|
|
|
730
773
|
this.emit("fileProgress", progress);
|
|
731
774
|
},
|
|
732
775
|
});
|
|
733
|
-
return msgpack.decode(
|
|
776
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
734
777
|
}
|
|
735
778
|
catch (err) {
|
|
736
779
|
return null;
|
|
@@ -741,8 +784,8 @@ export class Client extends EventEmitter {
|
|
|
741
784
|
name,
|
|
742
785
|
};
|
|
743
786
|
try {
|
|
744
|
-
const res = await ax.post(this.getHost() + "/emoji/" + serverID + "/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
745
|
-
return msgpack.decode(
|
|
787
|
+
const res = await this.ax.post(this.getHost() + "/emoji/" + serverID + "/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
788
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
746
789
|
}
|
|
747
790
|
catch (err) {
|
|
748
791
|
return null;
|
|
@@ -751,11 +794,11 @@ export class Client extends EventEmitter {
|
|
|
751
794
|
async retrieveOrCreateDevice() {
|
|
752
795
|
let device;
|
|
753
796
|
try {
|
|
754
|
-
const res = await ax.get(this.prefixes.HTTP +
|
|
797
|
+
const res = await this.ax.get(this.prefixes.HTTP +
|
|
755
798
|
this.host +
|
|
756
799
|
"/device/" +
|
|
757
800
|
XUtils.encodeHex(this.signKeys.publicKey));
|
|
758
|
-
device = msgpack.decode(
|
|
801
|
+
device = msgpack.decode(new Uint8Array(res.data));
|
|
759
802
|
}
|
|
760
803
|
catch (err) {
|
|
761
804
|
this.log.error(err.toString());
|
|
@@ -803,15 +846,15 @@ export class Client extends EventEmitter {
|
|
|
803
846
|
preKey: XUtils.encodeHex(this.xKeyRing.preKeys.keyPair.publicKey),
|
|
804
847
|
preKeySignature: XUtils.encodeHex(this.xKeyRing.preKeys.signature),
|
|
805
848
|
preKeyIndex: this.xKeyRing.preKeys.index,
|
|
806
|
-
deviceName:
|
|
849
|
+
deviceName: this.options?.deviceName ?? "unknown",
|
|
807
850
|
};
|
|
808
851
|
try {
|
|
809
|
-
const res = await ax.post(this.prefixes.HTTP +
|
|
852
|
+
const res = await this.ax.post(this.prefixes.HTTP +
|
|
810
853
|
this.host +
|
|
811
854
|
"/user/" +
|
|
812
855
|
userDetails.userID +
|
|
813
856
|
"/devices", msgpack.encode(devMsg), { headers: { "Content-Type": "application/msgpack" } });
|
|
814
|
-
return msgpack.decode(
|
|
857
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
815
858
|
}
|
|
816
859
|
catch (err) {
|
|
817
860
|
throw err;
|
|
@@ -819,10 +862,10 @@ export class Client extends EventEmitter {
|
|
|
819
862
|
}
|
|
820
863
|
async getToken(type) {
|
|
821
864
|
try {
|
|
822
|
-
const res = await ax.get(this.getHost() + "/token/" + type, {
|
|
865
|
+
const res = await this.ax.get(this.getHost() + "/token/" + type, {
|
|
823
866
|
responseType: "arraybuffer",
|
|
824
867
|
});
|
|
825
|
-
return msgpack.decode(
|
|
868
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
826
869
|
}
|
|
827
870
|
catch (err) {
|
|
828
871
|
this.log.warn(err.toString());
|
|
@@ -833,7 +876,7 @@ export class Client extends EventEmitter {
|
|
|
833
876
|
if (typeof FormData !== "undefined") {
|
|
834
877
|
const fpayload = new FormData();
|
|
835
878
|
fpayload.set("avatar", new Blob([new Uint8Array(avatar)]));
|
|
836
|
-
await ax.post(this.prefixes.HTTP +
|
|
879
|
+
await this.ax.post(this.prefixes.HTTP +
|
|
837
880
|
this.host +
|
|
838
881
|
"/avatar/" +
|
|
839
882
|
this.me.user().userID, fpayload, {
|
|
@@ -857,7 +900,7 @@ export class Client extends EventEmitter {
|
|
|
857
900
|
const payload = {
|
|
858
901
|
file: XUtils.encodeBase64(avatar),
|
|
859
902
|
};
|
|
860
|
-
await ax.post(this.prefixes.HTTP +
|
|
903
|
+
await this.ax.post(this.prefixes.HTTP +
|
|
861
904
|
this.host +
|
|
862
905
|
"/avatar/" +
|
|
863
906
|
this.me.user().userID +
|
|
@@ -869,12 +912,12 @@ export class Client extends EventEmitter {
|
|
|
869
912
|
* @returns - The list of IPermissions objects.
|
|
870
913
|
*/
|
|
871
914
|
async fetchPermissionList(serverID) {
|
|
872
|
-
const res = await ax.get(this.prefixes.HTTP +
|
|
915
|
+
const res = await this.ax.get(this.prefixes.HTTP +
|
|
873
916
|
this.host +
|
|
874
917
|
"/server/" +
|
|
875
918
|
serverID +
|
|
876
919
|
"/permissions");
|
|
877
|
-
return msgpack.decode(
|
|
920
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
878
921
|
}
|
|
879
922
|
/**
|
|
880
923
|
* Gets all permissions for the logged in user.
|
|
@@ -882,17 +925,17 @@ export class Client extends EventEmitter {
|
|
|
882
925
|
* @returns - The list of IPermissions objects.
|
|
883
926
|
*/
|
|
884
927
|
async getPermissions() {
|
|
885
|
-
const res = await ax.get(this.getHost() + "/user/" + this.getUser().userID + "/permissions");
|
|
886
|
-
return msgpack.decode(
|
|
928
|
+
const res = await this.ax.get(this.getHost() + "/user/" + this.getUser().userID + "/permissions");
|
|
929
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
887
930
|
}
|
|
888
931
|
async deletePermission(permissionID) {
|
|
889
|
-
await ax.delete(this.getHost() + "/permission/" + permissionID);
|
|
932
|
+
await this.ax.delete(this.getHost() + "/permission/" + permissionID);
|
|
890
933
|
}
|
|
891
934
|
async retrieveFile(fileID, key) {
|
|
892
935
|
try {
|
|
893
|
-
const detailsRes = await ax.get(this.getHost() + "/file/" + fileID + "/details");
|
|
894
|
-
const details = msgpack.decode(
|
|
895
|
-
const res = await ax.get(this.getHost() + "/file/" + fileID, {
|
|
936
|
+
const detailsRes = await this.ax.get(this.getHost() + "/file/" + fileID + "/details");
|
|
937
|
+
const details = msgpack.decode(new Uint8Array(detailsRes.data));
|
|
938
|
+
const res = await this.ax.get(this.getHost() + "/file/" + fileID, {
|
|
896
939
|
onDownloadProgress: (progressEvent) => {
|
|
897
940
|
const percentCompleted = Math.round((progressEvent.loaded * 100) /
|
|
898
941
|
(progressEvent.total ?? 1));
|
|
@@ -908,11 +951,11 @@ export class Client extends EventEmitter {
|
|
|
908
951
|
},
|
|
909
952
|
});
|
|
910
953
|
const fileData = res.data;
|
|
911
|
-
const decrypted = nacl.secretbox.open(Uint8Array
|
|
954
|
+
const decrypted = nacl.secretbox.open(new Uint8Array(fileData), XUtils.decodeHex(details.nonce), XUtils.decodeHex(key));
|
|
912
955
|
if (decrypted) {
|
|
913
956
|
const resp = {
|
|
914
957
|
details,
|
|
915
|
-
data:
|
|
958
|
+
data: new Uint8Array(decrypted),
|
|
916
959
|
};
|
|
917
960
|
return resp;
|
|
918
961
|
}
|
|
@@ -923,14 +966,14 @@ export class Client extends EventEmitter {
|
|
|
923
966
|
}
|
|
924
967
|
}
|
|
925
968
|
async deleteServer(serverID) {
|
|
926
|
-
await ax.delete(this.getHost() + "/server/" + serverID);
|
|
969
|
+
await this.ax.delete(this.getHost() + "/server/" + serverID);
|
|
927
970
|
}
|
|
928
971
|
/**
|
|
929
972
|
* Initializes the keyring. This must be called before anything else.
|
|
930
973
|
*/
|
|
931
974
|
async init() {
|
|
932
975
|
if (this.hasInit) {
|
|
933
|
-
|
|
976
|
+
throw new Error("You should only call init() once.");
|
|
934
977
|
}
|
|
935
978
|
this.hasInit = true;
|
|
936
979
|
await this.populateKeyRing();
|
|
@@ -947,21 +990,21 @@ export class Client extends EventEmitter {
|
|
|
947
990
|
this.emit("ready");
|
|
948
991
|
}
|
|
949
992
|
async deleteChannel(channelID) {
|
|
950
|
-
await ax.delete(this.getHost() + "/channel/" + channelID);
|
|
993
|
+
await this.ax.delete(this.getHost() + "/channel/" + channelID);
|
|
951
994
|
}
|
|
952
995
|
// returns the file details and the encryption key
|
|
953
996
|
async createFile(file) {
|
|
954
|
-
this.log.info("Creating file, size: " + formatBytes(
|
|
997
|
+
this.log.info("Creating file, size: " + formatBytes(file.byteLength));
|
|
955
998
|
const nonce = xMakeNonce();
|
|
956
999
|
const key = nacl.box.keyPair();
|
|
957
1000
|
const box = nacl.secretbox(Uint8Array.from(file), nonce, key.secretKey);
|
|
958
|
-
this.log.info("Encrypted size: " + formatBytes(
|
|
1001
|
+
this.log.info("Encrypted size: " + formatBytes(box.byteLength));
|
|
959
1002
|
if (typeof FormData !== "undefined") {
|
|
960
1003
|
const fpayload = new FormData();
|
|
961
1004
|
fpayload.set("owner", this.getDevice().deviceID);
|
|
962
1005
|
fpayload.set("nonce", XUtils.encodeHex(nonce));
|
|
963
1006
|
fpayload.set("file", new Blob([new Uint8Array(box)]));
|
|
964
|
-
const fres = await ax.post(this.getHost() + "/file", fpayload, {
|
|
1007
|
+
const fres = await this.ax.post(this.getHost() + "/file", fpayload, {
|
|
965
1008
|
headers: { "Content-Type": "multipart/form-data" },
|
|
966
1009
|
onUploadProgress: (progressEvent) => {
|
|
967
1010
|
const percentCompleted = Math.round((progressEvent.loaded * 100) /
|
|
@@ -977,7 +1020,7 @@ export class Client extends EventEmitter {
|
|
|
977
1020
|
this.emit("fileProgress", progress);
|
|
978
1021
|
},
|
|
979
1022
|
});
|
|
980
|
-
const fcreatedFile = msgpack.decode(
|
|
1023
|
+
const fcreatedFile = msgpack.decode(new Uint8Array(fres.data));
|
|
981
1024
|
return [fcreatedFile, XUtils.encodeHex(key.secretKey)];
|
|
982
1025
|
}
|
|
983
1026
|
const payload = {
|
|
@@ -985,13 +1028,13 @@ export class Client extends EventEmitter {
|
|
|
985
1028
|
nonce: XUtils.encodeHex(nonce),
|
|
986
1029
|
file: XUtils.encodeBase64(box),
|
|
987
1030
|
};
|
|
988
|
-
const res = await ax.post(this.getHost() + "/file/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
989
|
-
const createdFile = msgpack.decode(
|
|
1031
|
+
const res = await this.ax.post(this.getHost() + "/file/json", msgpack.encode(payload), { headers: { "Content-Type": "application/msgpack" } });
|
|
1032
|
+
const createdFile = msgpack.decode(new Uint8Array(res.data));
|
|
990
1033
|
return [createdFile, XUtils.encodeHex(key.secretKey)];
|
|
991
1034
|
}
|
|
992
1035
|
async getUserList(channelID) {
|
|
993
|
-
const res = await ax.post(this.getHost() + "/userList/" + channelID);
|
|
994
|
-
return msgpack.decode(
|
|
1036
|
+
const res = await this.ax.post(this.getHost() + "/userList/" + channelID);
|
|
1037
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
995
1038
|
}
|
|
996
1039
|
async markSessionVerified(sessionID) {
|
|
997
1040
|
return this.database.markSessionVerified(sessionID);
|
|
@@ -1040,7 +1083,7 @@ export class Client extends EventEmitter {
|
|
|
1040
1083
|
const { status } = result;
|
|
1041
1084
|
if (status === "rejected") {
|
|
1042
1085
|
this.log.warn("Message failed.");
|
|
1043
|
-
this.log.warn(result);
|
|
1086
|
+
this.log.warn(JSON.stringify(result));
|
|
1044
1087
|
}
|
|
1045
1088
|
}
|
|
1046
1089
|
});
|
|
@@ -1073,14 +1116,14 @@ export class Client extends EventEmitter {
|
|
|
1073
1116
|
const { status } = result;
|
|
1074
1117
|
if (status === "rejected") {
|
|
1075
1118
|
this.log.warn("Message failed.");
|
|
1076
|
-
this.log.warn(result);
|
|
1119
|
+
this.log.warn(JSON.stringify(result));
|
|
1077
1120
|
}
|
|
1078
1121
|
}
|
|
1079
1122
|
});
|
|
1080
1123
|
}
|
|
1081
1124
|
async createServer(name) {
|
|
1082
|
-
const res = await ax.post(this.getHost() + "/server/" + btoa(name));
|
|
1083
|
-
return msgpack.decode(
|
|
1125
|
+
const res = await this.ax.post(this.getHost() + "/server/" + globalThis.btoa(name));
|
|
1126
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1084
1127
|
}
|
|
1085
1128
|
async forward(message) {
|
|
1086
1129
|
const copy = { ...message };
|
|
@@ -1109,7 +1152,7 @@ export class Client extends EventEmitter {
|
|
|
1109
1152
|
const { status } = result;
|
|
1110
1153
|
if (status === "rejected") {
|
|
1111
1154
|
this.log.warn("Message failed.");
|
|
1112
|
-
this.log.warn(result);
|
|
1155
|
+
this.log.warn(JSON.stringify(result));
|
|
1113
1156
|
}
|
|
1114
1157
|
}
|
|
1115
1158
|
});
|
|
@@ -1203,13 +1246,13 @@ export class Client extends EventEmitter {
|
|
|
1203
1246
|
return this.database.getAllSessions();
|
|
1204
1247
|
}
|
|
1205
1248
|
async getServerList() {
|
|
1206
|
-
const res = await ax.get(this.getHost() + "/user/" + this.getUser().userID + "/servers");
|
|
1207
|
-
return msgpack.decode(
|
|
1249
|
+
const res = await this.ax.get(this.getHost() + "/user/" + this.getUser().userID + "/servers");
|
|
1250
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1208
1251
|
}
|
|
1209
1252
|
async createChannel(name, serverID) {
|
|
1210
1253
|
const body = { name };
|
|
1211
|
-
const res = await ax.post(this.getHost() + "/server/" + serverID + "/channels", msgpack.encode(body), { headers: { "Content-Type": "application/msgpack" } });
|
|
1212
|
-
return msgpack.decode(
|
|
1254
|
+
const res = await this.ax.post(this.getHost() + "/server/" + serverID + "/channels", msgpack.encode(body), { headers: { "Content-Type": "application/msgpack" } });
|
|
1255
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1213
1256
|
}
|
|
1214
1257
|
async getDeviceByID(deviceID) {
|
|
1215
1258
|
if (this.deviceRecords[deviceID]) {
|
|
@@ -1223,9 +1266,9 @@ export class Client extends EventEmitter {
|
|
|
1223
1266
|
return device;
|
|
1224
1267
|
}
|
|
1225
1268
|
try {
|
|
1226
|
-
const res = await ax.get(this.getHost() + "/device/" + deviceID);
|
|
1269
|
+
const res = await this.ax.get(this.getHost() + "/device/" + deviceID);
|
|
1227
1270
|
this.log.info("Retrieved device from server.");
|
|
1228
|
-
const fetchedDevice = msgpack.decode(
|
|
1271
|
+
const fetchedDevice = msgpack.decode(new Uint8Array(res.data));
|
|
1229
1272
|
this.deviceRecords[deviceID] = fetchedDevice;
|
|
1230
1273
|
await this.database.saveDevice(fetchedDevice);
|
|
1231
1274
|
return fetchedDevice;
|
|
@@ -1238,7 +1281,7 @@ export class Client extends EventEmitter {
|
|
|
1238
1281
|
if (deviceID === this.getDevice().deviceID) {
|
|
1239
1282
|
throw new Error("You can't delete the device you're logged in to.");
|
|
1240
1283
|
}
|
|
1241
|
-
await ax.delete(this.prefixes.HTTP +
|
|
1284
|
+
await this.ax.delete(this.prefixes.HTTP +
|
|
1242
1285
|
this.host +
|
|
1243
1286
|
"/user/" +
|
|
1244
1287
|
this.getUser().userID +
|
|
@@ -1247,8 +1290,8 @@ export class Client extends EventEmitter {
|
|
|
1247
1290
|
}
|
|
1248
1291
|
async getMultiUserDeviceList(userIDs) {
|
|
1249
1292
|
try {
|
|
1250
|
-
const res = await ax.post(this.getHost() + "/deviceList", msgpack.encode(userIDs), { headers: { "Content-Type": "application/msgpack" } });
|
|
1251
|
-
const devices = msgpack.decode(
|
|
1293
|
+
const res = await this.ax.post(this.getHost() + "/deviceList", msgpack.encode(userIDs), { headers: { "Content-Type": "application/msgpack" } });
|
|
1294
|
+
const devices = msgpack.decode(new Uint8Array(res.data));
|
|
1252
1295
|
for (const device of devices) {
|
|
1253
1296
|
this.deviceRecords[device.deviceID] = device;
|
|
1254
1297
|
}
|
|
@@ -1260,8 +1303,8 @@ export class Client extends EventEmitter {
|
|
|
1260
1303
|
}
|
|
1261
1304
|
async getUserDeviceList(userID) {
|
|
1262
1305
|
try {
|
|
1263
|
-
const res = await ax.get(this.getHost() + "/user/" + userID + "/devices");
|
|
1264
|
-
const devices = msgpack.decode(
|
|
1306
|
+
const res = await this.ax.get(this.getHost() + "/user/" + userID + "/devices");
|
|
1307
|
+
const devices = msgpack.decode(new Uint8Array(res.data));
|
|
1265
1308
|
for (const device of devices) {
|
|
1266
1309
|
this.deviceRecords[device.deviceID] = device;
|
|
1267
1310
|
}
|
|
@@ -1273,8 +1316,8 @@ export class Client extends EventEmitter {
|
|
|
1273
1316
|
}
|
|
1274
1317
|
async getServerByID(serverID) {
|
|
1275
1318
|
try {
|
|
1276
|
-
const res = await ax.get(this.getHost() + "/server/" + serverID);
|
|
1277
|
-
return msgpack.decode(
|
|
1319
|
+
const res = await this.ax.get(this.getHost() + "/server/" + serverID);
|
|
1320
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1278
1321
|
}
|
|
1279
1322
|
catch (err) {
|
|
1280
1323
|
return null;
|
|
@@ -1282,16 +1325,16 @@ export class Client extends EventEmitter {
|
|
|
1282
1325
|
}
|
|
1283
1326
|
async getChannelByID(channelID) {
|
|
1284
1327
|
try {
|
|
1285
|
-
const res = await ax.get(this.getHost() + "/channel/" + channelID);
|
|
1286
|
-
return msgpack.decode(
|
|
1328
|
+
const res = await this.ax.get(this.getHost() + "/channel/" + channelID);
|
|
1329
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1287
1330
|
}
|
|
1288
1331
|
catch (err) {
|
|
1289
1332
|
return null;
|
|
1290
1333
|
}
|
|
1291
1334
|
}
|
|
1292
1335
|
async getChannelList(serverID) {
|
|
1293
|
-
const res = await ax.get(this.getHost() + "/server/" + serverID + "/channels");
|
|
1294
|
-
return msgpack.decode(
|
|
1336
|
+
const res = await this.ax.get(this.getHost() + "/server/" + serverID + "/channels");
|
|
1337
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1295
1338
|
}
|
|
1296
1339
|
/* Get the currently logged in user. You cannot call this until
|
|
1297
1340
|
after the auth event is emitted. */
|
|
@@ -1318,8 +1361,8 @@ export class Client extends EventEmitter {
|
|
|
1318
1361
|
return [this.userRecords[userIdentifier], null];
|
|
1319
1362
|
}
|
|
1320
1363
|
try {
|
|
1321
|
-
const res = await ax.get(this.getHost() + "/user/" + userIdentifier);
|
|
1322
|
-
const userRecord = msgpack.decode(
|
|
1364
|
+
const res = await this.ax.get(this.getHost() + "/user/" + userIdentifier);
|
|
1365
|
+
const userRecord = msgpack.decode(new Uint8Array(res.data));
|
|
1323
1366
|
this.userRecords[userIdentifier] = userRecord;
|
|
1324
1367
|
return [userRecord, null];
|
|
1325
1368
|
}
|
|
@@ -1804,13 +1847,21 @@ export class Client extends EventEmitter {
|
|
|
1804
1847
|
if (!this.token) {
|
|
1805
1848
|
throw new Error("No token found, did you call login()?");
|
|
1806
1849
|
}
|
|
1807
|
-
|
|
1850
|
+
const wsUrl = this.prefixes.WS + this.host + "/socket";
|
|
1851
|
+
// Auth sent as first message after open
|
|
1852
|
+
this.conn = new this.adapters.WebSocket(wsUrl);
|
|
1808
1853
|
this.conn.on("open", () => {
|
|
1809
1854
|
this.log.info("Connection opened.");
|
|
1855
|
+
// Send auth as first message before anything else.
|
|
1856
|
+
this.conn.send(JSON.stringify({ type: "auth", token: this.token }));
|
|
1810
1857
|
this.pingInterval = setInterval(this.ping.bind(this), 15000);
|
|
1811
1858
|
});
|
|
1812
1859
|
this.conn.on("close", () => {
|
|
1813
1860
|
this.log.info("Connection closed.");
|
|
1861
|
+
if (this.pingInterval) {
|
|
1862
|
+
clearInterval(this.pingInterval);
|
|
1863
|
+
this.pingInterval = null;
|
|
1864
|
+
}
|
|
1814
1865
|
if (!this.manuallyClosing) {
|
|
1815
1866
|
this.emit("disconnect");
|
|
1816
1867
|
}
|
|
@@ -1820,8 +1871,8 @@ export class Client extends EventEmitter {
|
|
|
1820
1871
|
});
|
|
1821
1872
|
this.conn.on("message", async (message) => {
|
|
1822
1873
|
const [header, msg] = XUtils.unpackMessage(message);
|
|
1823
|
-
this.log.debug(
|
|
1824
|
-
this.log.debug(
|
|
1874
|
+
this.log.debug(pc.red(pc.bold("INH ") + XUtils.encodeHex(header)));
|
|
1875
|
+
this.log.debug(pc.red(pc.bold("IN ") + JSON.stringify(msg, null, 4)));
|
|
1825
1876
|
switch (msg.type) {
|
|
1826
1877
|
case "ping":
|
|
1827
1878
|
this.pong(msg.transmissionID);
|
|
@@ -1894,12 +1945,12 @@ export class Client extends EventEmitter {
|
|
|
1894
1945
|
}
|
|
1895
1946
|
this.log.info("fetching mail for device " + this.getDevice().deviceID);
|
|
1896
1947
|
try {
|
|
1897
|
-
const res = await ax.post(this.getHost() +
|
|
1948
|
+
const res = await this.ax.post(this.getHost() +
|
|
1898
1949
|
"/device/" +
|
|
1899
1950
|
this.getDevice().deviceID +
|
|
1900
1951
|
"/mail");
|
|
1901
1952
|
const inbox = msgpack
|
|
1902
|
-
.decode(
|
|
1953
|
+
.decode(new Uint8Array(res.data))
|
|
1903
1954
|
.sort((a, b) => b[2].getTime() - a[2].getTime());
|
|
1904
1955
|
for (const mailDetails of inbox) {
|
|
1905
1956
|
const [mailHeader, mailBody, timestamp] = mailDetails;
|
|
@@ -1925,21 +1976,21 @@ export class Client extends EventEmitter {
|
|
|
1925
1976
|
await sleep(i);
|
|
1926
1977
|
i *= 2;
|
|
1927
1978
|
}
|
|
1928
|
-
this.log.debug(
|
|
1929
|
-
XUtils.encodeHex(header || XUtils.emptyHeader()));
|
|
1930
|
-
this.log.debug(
|
|
1979
|
+
this.log.debug(pc.red(pc.bold("OUTH ") +
|
|
1980
|
+
XUtils.encodeHex(header || XUtils.emptyHeader())));
|
|
1981
|
+
this.log.debug(pc.red(pc.bold("OUT ") + JSON.stringify(msg, null, 4)));
|
|
1931
1982
|
this.conn.send(XUtils.packMessage(msg, header));
|
|
1932
1983
|
}
|
|
1933
1984
|
async retrieveKeyBundle(deviceID) {
|
|
1934
|
-
const res = await ax.post(this.getHost() + "/device/" + deviceID + "/keyBundle");
|
|
1935
|
-
return msgpack.decode(
|
|
1985
|
+
const res = await this.ax.post(this.getHost() + "/device/" + deviceID + "/keyBundle");
|
|
1986
|
+
return msgpack.decode(new Uint8Array(res.data));
|
|
1936
1987
|
}
|
|
1937
1988
|
async getOTKCount() {
|
|
1938
|
-
const res = await ax.get(this.getHost() +
|
|
1989
|
+
const res = await this.ax.get(this.getHost() +
|
|
1939
1990
|
"/device/" +
|
|
1940
1991
|
this.getDevice().deviceID +
|
|
1941
1992
|
"/otk/count");
|
|
1942
|
-
return msgpack.decode(
|
|
1993
|
+
return msgpack.decode(new Uint8Array(res.data)).count;
|
|
1943
1994
|
}
|
|
1944
1995
|
async submitOTK(amount) {
|
|
1945
1996
|
const otks = [];
|
|
@@ -1950,7 +2001,7 @@ export class Client extends EventEmitter {
|
|
|
1950
2001
|
const t1 = performance.now();
|
|
1951
2002
|
this.log.info("Generated " + amount + " one time keys in " + (t1 - t0) + " ms.");
|
|
1952
2003
|
const savedKeys = await this.database.savePreKeys(otks, true);
|
|
1953
|
-
await ax.post(this.getHost() + "/device/" + this.getDevice().deviceID + "/otk", msgpack.encode(savedKeys.map((key) => this.censorPreKey(key))), {
|
|
2004
|
+
await this.ax.post(this.getHost() + "/device/" + this.getDevice().deviceID + "/otk", msgpack.encode(savedKeys.map((key) => this.censorPreKey(key))), {
|
|
1954
2005
|
headers: { "Content-Type": "application/msgpack" },
|
|
1955
2006
|
});
|
|
1956
2007
|
}
|