'init'
This commit is contained in:
BIN
app/providers/ProtocolProvider/protocol/.DS_Store
vendored
Normal file
BIN
app/providers/ProtocolProvider/protocol/.DS_Store
vendored
Normal file
Binary file not shown.
28
app/providers/ProtocolProvider/protocol/packet.ts
Normal file
28
app/providers/ProtocolProvider/protocol/packet.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import Stream from "./stream";
|
||||
|
||||
/**
|
||||
* Packet abstract class
|
||||
*/
|
||||
export default abstract class Packet {
|
||||
|
||||
/**
|
||||
* Get the packet ID
|
||||
* @returns packet ID
|
||||
*/
|
||||
public abstract getPacketId(): number;
|
||||
/**
|
||||
* Use the stream to read the packet and fill structure
|
||||
* @param stream stream
|
||||
*/
|
||||
public abstract _receive(stream: Stream): void;
|
||||
/**
|
||||
* Use the stream to write the packet and return the stream
|
||||
* @returns stream
|
||||
*/
|
||||
public abstract _send(): Promise<Stream> | Stream;
|
||||
|
||||
public clone(): Packet {
|
||||
return new (this as any).constructor();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketAppUpdate extends Packet {
|
||||
|
||||
private url: string = "";
|
||||
private version: string = "";
|
||||
private kernelVersionRequired: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x0E;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.url = stream.readString();
|
||||
this.version = stream.readString();
|
||||
this.kernelVersionRequired = stream.readString();
|
||||
}
|
||||
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
let stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.url);
|
||||
stream.writeString(this.version);
|
||||
stream.writeString(this.kernelVersionRequired);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getUrl(): string {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public setUrl(url: string): void {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public getVersion(): string {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public setVersion(version: string): void {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public getKernelVersionRequired(): string {
|
||||
return this.kernelVersionRequired;
|
||||
}
|
||||
|
||||
public setKernelVersionRequired(kernelVersionRequired: string): void {
|
||||
this.kernelVersionRequired = kernelVersionRequired;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketAvatar extends Packet {
|
||||
|
||||
private privateKey : string = "";
|
||||
private fromPublicKey: string = "";
|
||||
private toPublicKey: string = "";
|
||||
private blob: string = "";
|
||||
|
||||
private chachaKey: string = "";
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x0C;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.privateKey = stream.readString();
|
||||
this.fromPublicKey = stream.readString();
|
||||
this.toPublicKey = stream.readString();
|
||||
this.chachaKey = stream.readString();
|
||||
this.blob = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.fromPublicKey);
|
||||
stream.writeString(this.toPublicKey);
|
||||
stream.writeString(this.chachaKey);
|
||||
stream.writeString(this.blob);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setFromPublicKey(fromPublicKey: string): void {
|
||||
this.fromPublicKey = fromPublicKey;
|
||||
}
|
||||
|
||||
public setToPublicKey(toPublicKey: string): void {
|
||||
this.toPublicKey = toPublicKey;
|
||||
}
|
||||
|
||||
public getFromPublicKey(): string {
|
||||
return this.fromPublicKey;
|
||||
}
|
||||
|
||||
public getToPublicKey(): string {
|
||||
return this.toPublicKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(hash: string): void {
|
||||
this.privateKey = hash;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
public setBlob(blob: string): void {
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
public getBlob(): string {
|
||||
return this.blob;
|
||||
}
|
||||
|
||||
public setChachaKey(key: string): void {
|
||||
this.chachaKey = key;
|
||||
}
|
||||
|
||||
public getChachaKey(): string {
|
||||
return this.chachaKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketCreateGroup extends Packet {
|
||||
|
||||
private groupId: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x11;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.groupId = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.groupId);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setGroupId(groupId: string) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public getGroupId(): string {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketDelivery extends Packet {
|
||||
|
||||
private messageId: string = "";
|
||||
private toPublicKey: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x08;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.toPublicKey = stream.readString();
|
||||
this.messageId = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.toPublicKey);
|
||||
stream.writeString(this.messageId);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setMessageId(messageId: string) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public getMessageId(): string {
|
||||
return this.messageId;
|
||||
}
|
||||
|
||||
public setToPublicKey(toPublicKey: string) {
|
||||
this.toPublicKey = toPublicKey;
|
||||
}
|
||||
|
||||
public getToPublicKey(): string {
|
||||
return this.toPublicKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketGroupBan extends Packet {
|
||||
|
||||
private groupId: string = "";
|
||||
private publicKey: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x16;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.groupId = stream.readString();
|
||||
this.publicKey = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.groupId);
|
||||
stream.writeString(this.publicKey);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getGroupId(): string {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public getPublicKey(): string {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
public setGroupId(groupId: string): void {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public setPublicKey(publicKey: string): void {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
|
||||
export class PacketGroupInfo extends Packet {
|
||||
|
||||
private groupId: string = "";
|
||||
private members: string[] = [];
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x12;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.groupId = stream.readString();
|
||||
const membersCount = stream.readInt16();
|
||||
this.members = [];
|
||||
for(let i = 0; i < membersCount; i++) {
|
||||
this.members.push(stream.readString());
|
||||
}
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.groupId);
|
||||
stream.writeInt16(this.members.length);
|
||||
this.members.forEach((member) => {
|
||||
stream.writeString(member);
|
||||
});
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getGroupId(): string {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public setGroupId(groupId: string): void {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public setMembers(members: string[]): void {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public getMembers(): string[] {
|
||||
return this.members;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import Packet from "../packet"
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum GroupStatus {
|
||||
JOINED = 0,
|
||||
INVALID = 1,
|
||||
NOT_JOINED = 2,
|
||||
BANNED = 3
|
||||
}
|
||||
|
||||
export class PacketGroupInviteInfo extends Packet {
|
||||
|
||||
private groupId: string = "";
|
||||
private membersCount = 0;
|
||||
private groupStatus: GroupStatus = GroupStatus.NOT_JOINED;
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x13;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.groupId = stream.readString();
|
||||
this.membersCount = stream.readInt16();
|
||||
this.groupStatus = stream.readInt8();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.groupId);
|
||||
stream.writeInt16(this.membersCount);
|
||||
stream.writeInt8(this.groupStatus);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setGroupId(groupId: string) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public getGroupId(): string {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public setMembersCount(count: number) {
|
||||
this.membersCount = count;
|
||||
}
|
||||
|
||||
public getMembersCount(): number {
|
||||
return this.membersCount;
|
||||
}
|
||||
|
||||
public setGroupStatus(status: GroupStatus) {
|
||||
this.groupStatus = status;
|
||||
}
|
||||
|
||||
public getGroupStatus(): GroupStatus {
|
||||
return this.groupStatus;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum GroupStatus {
|
||||
JOINED = 0,
|
||||
INVALID = 1,
|
||||
NOT_JOINED = 2,
|
||||
BANNED = 3
|
||||
}
|
||||
|
||||
export class PacketGroupJoin extends Packet {
|
||||
|
||||
private groupId: string = "";
|
||||
private groupStatus: GroupStatus = GroupStatus.NOT_JOINED;
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x14;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.groupId = stream.readString();
|
||||
this.groupStatus = stream.readInt8();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.groupId);
|
||||
stream.writeInt8(this.groupStatus);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setGroupId(groupId: string) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public getGroupId(): string {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public setGroupStatus(groupStatus: GroupStatus) {
|
||||
this.groupStatus = groupStatus;
|
||||
}
|
||||
|
||||
public getGroupStatus(): GroupStatus {
|
||||
return this.groupStatus;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketGroupLeave extends Packet {
|
||||
|
||||
private groupId: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x15;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.groupId = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.groupId);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setGroupId(groupId: string) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public getGroupId(): string {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum HandshakeState {
|
||||
COMPLETED,
|
||||
NEED_DEVICE_VERIFICATION
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
deviceId: string;
|
||||
deviceName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hadshake packet
|
||||
* ID: 0x00
|
||||
*
|
||||
* The handshake packet is the first packet sent by the client to the server.
|
||||
* It contains the hash of the client's public key and the public key itself.
|
||||
*/
|
||||
export default class PacketHandshake extends Packet {
|
||||
private privateKey: string = "";
|
||||
private publicKey: string = "";
|
||||
private protocolVersion: number = 1;
|
||||
/**
|
||||
* Interval seconds
|
||||
*/
|
||||
private heartbeatInterval : number = 15;
|
||||
private device: Device = {
|
||||
deviceId: "",
|
||||
deviceName: ""
|
||||
};
|
||||
private handshakeState:
|
||||
HandshakeState = HandshakeState.NEED_DEVICE_VERIFICATION;
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.privateKey = stream.readString();
|
||||
this.publicKey = stream.readString();
|
||||
this.protocolVersion = stream.readInt8();
|
||||
this.heartbeatInterval = stream.readInt8();
|
||||
this.device = {
|
||||
deviceId: stream.readString(),
|
||||
deviceName: stream.readString()
|
||||
}
|
||||
this.handshakeState = stream.readInt8();
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.publicKey);
|
||||
stream.writeInt8(this.protocolVersion);
|
||||
stream.writeInt8(this.heartbeatInterval);
|
||||
stream.writeString(this.device.deviceId);
|
||||
stream.writeString(this.device.deviceName);
|
||||
stream.writeInt8(this.handshakeState);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
public getPublicKey(): string {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public setPublicKey(publicKey: string): void {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public getProtocolVersion(): number {
|
||||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
public setProtocolVersion(protocolVersion: number): void {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public getHeartbeatInterval(): number {
|
||||
return this.heartbeatInterval;
|
||||
}
|
||||
|
||||
public setHeartbeatInterval(heartbeatInterval: number): void {
|
||||
this.heartbeatInterval = heartbeatInterval;
|
||||
}
|
||||
|
||||
public getDevice(): Device {
|
||||
return this.device;
|
||||
}
|
||||
|
||||
public setDevice(device: Device): void {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public getHandshakeState(): HandshakeState {
|
||||
return this.handshakeState;
|
||||
}
|
||||
|
||||
public setHandshakeState(handshakeState: HandshakeState): void {
|
||||
this.handshakeState = handshakeState;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketKernelUpdate extends Packet {
|
||||
|
||||
private url: string = "";
|
||||
private version: string = "";
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x0D;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.url = stream.readString();
|
||||
this.version = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
let stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.url);
|
||||
stream.writeString(this.version);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getUrl(): string {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public setUrl(url: string): void {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public getVersion(): string {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public setVersion(version: string): void {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum AttachmentType {
|
||||
IMAGE = 0,
|
||||
MESSAGES = 1,
|
||||
FILE = 2,
|
||||
AVATAR = 3
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
id: string;
|
||||
blob: string;
|
||||
type: AttachmentType;
|
||||
preview: string;
|
||||
}
|
||||
|
||||
export class PacketMessage extends Packet {
|
||||
|
||||
private fromPublicKey: string = "";
|
||||
private toPublicKey: string = "";
|
||||
private content: string = "";
|
||||
private chachaKey: string = "";
|
||||
private timestamp: number = 0;
|
||||
private privateKey: string = "";
|
||||
private messageId: string = "";
|
||||
/**
|
||||
* Закодированный с помощью AES ключ chacha, нужен
|
||||
* для последующей синхронизации своих же сообщений
|
||||
*/
|
||||
private aesChachaKey: string = "";
|
||||
|
||||
private attachments: Attachment[] = [];
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x06;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.fromPublicKey = stream.readString();
|
||||
this.toPublicKey = stream.readString();
|
||||
this.content = stream.readString();
|
||||
this.chachaKey = stream.readString();
|
||||
this.timestamp = stream.readInt64();
|
||||
this.privateKey = stream.readString();
|
||||
this.messageId = stream.readString();
|
||||
let attachmentsCount = stream.readInt8();
|
||||
for(let i = 0; i < attachmentsCount; i++){
|
||||
let id = stream.readString();
|
||||
let preview = stream.readString();
|
||||
let blob = stream.readString();
|
||||
let type = stream.readInt8() as AttachmentType;
|
||||
this.attachments.push({id, preview, type, blob});
|
||||
}
|
||||
this.aesChachaKey = stream.readString();
|
||||
}
|
||||
|
||||
public async _send(): Promise<Stream> {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.fromPublicKey);
|
||||
stream.writeString(this.toPublicKey);
|
||||
stream.writeString(this.content);
|
||||
stream.writeString(this.chachaKey);
|
||||
stream.writeInt64(this.timestamp);
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.messageId);
|
||||
stream.writeInt8(this.attachments.length);
|
||||
for(let i = 0; i < this.attachments.length; i++){
|
||||
stream.writeString(this.attachments[i].id);
|
||||
stream.writeString(this.attachments[i].preview);
|
||||
stream.writeString(this.attachments[i].blob);
|
||||
stream.writeInt8(this.attachments[i].type);
|
||||
}
|
||||
stream.writeString(this.aesChachaKey);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setAttachments(attachments: Attachment[]): void {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
public getAttachments(): Attachment[] {
|
||||
return this.attachments;
|
||||
}
|
||||
|
||||
public setMessageId(messageId: string): void {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public getMessageId(): string {
|
||||
return this.messageId;
|
||||
}
|
||||
|
||||
public setTimestamp(timestamp: number): void {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public getTimestamp() : number {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public getFromPublicKey(): string {
|
||||
return this.fromPublicKey;
|
||||
}
|
||||
|
||||
public getToPublicKey(): string {
|
||||
return this.toPublicKey;
|
||||
}
|
||||
|
||||
public getContent(): string {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public getChachaKey(): string {
|
||||
return this.chachaKey;
|
||||
}
|
||||
|
||||
public setFromPublicKey(fromPublicKey: string): void {
|
||||
this.fromPublicKey = fromPublicKey;
|
||||
}
|
||||
|
||||
public setToPublicKey(toPublicKey: string): void {
|
||||
this.toPublicKey = toPublicKey;
|
||||
}
|
||||
|
||||
public setContent(content: string): void {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public setChachaKey(chachaKey: string): void {
|
||||
this.chachaKey = chachaKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
public getAesChachaKey() : string {
|
||||
return this.aesChachaKey;
|
||||
}
|
||||
|
||||
public setAesChachaKey(aesChachaKey: string) {
|
||||
this.aesChachaKey = aesChachaKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum OnlineState {
|
||||
ONLINE,
|
||||
OFFLINE
|
||||
}
|
||||
|
||||
export interface PublicKeyOnlineState {
|
||||
publicKey: string;
|
||||
state: OnlineState;
|
||||
}
|
||||
|
||||
export class PacketOnlineState extends Packet {
|
||||
|
||||
private publicKeysState: PublicKeyOnlineState[] = [];
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x05;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
const publicKeyCount = stream.readInt8();
|
||||
for (let i = 0; i < publicKeyCount; i++) {
|
||||
const publicKey = stream.readString();
|
||||
const state = stream.readBoolean() ? OnlineState.ONLINE : OnlineState.OFFLINE;
|
||||
this.publicKeysState.push({ publicKey, state });
|
||||
}
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeInt8(this.publicKeysState.length);
|
||||
for (const publicKeyState of this.publicKeysState) {
|
||||
stream.writeString(publicKeyState.publicKey);
|
||||
stream.writeBoolean(publicKeyState.state === OnlineState.ONLINE);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
public addPublicKeyState(publicKey: string, state: OnlineState): void {
|
||||
this.publicKeysState.push({ publicKey, state });
|
||||
}
|
||||
|
||||
public getPublicKeysState(): PublicKeyOnlineState[] {
|
||||
return this.publicKeysState;
|
||||
}
|
||||
|
||||
public setPublicKeysState(publicKeysState: PublicKeyOnlineState[]): void {
|
||||
this.publicKeysState = publicKeysState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketOnlineSubscribe extends Packet {
|
||||
|
||||
private privateKey : string = "";
|
||||
private publicKeys: string[] = [];
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x04;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.privateKey = stream.readString();
|
||||
const keysCount = stream.readInt16();
|
||||
for (let i = 0; i < keysCount; i++) {
|
||||
this.publicKeys.push(stream.readString());
|
||||
}
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeInt16(this.publicKeys.length);
|
||||
for (const key of this.publicKeys) {
|
||||
stream.writeString(key);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public getPublicKeys(): string[] {
|
||||
return this.publicKeys;
|
||||
}
|
||||
|
||||
public setPublicKeys(publicKeys: string[]): void {
|
||||
this.publicKeys = publicKeys;
|
||||
}
|
||||
|
||||
public addPublicKey(publicKey: string): void {
|
||||
this.publicKeys.push(publicKey);
|
||||
}
|
||||
|
||||
public removePublicKey(publicKey: string): void {
|
||||
const index = this.publicKeys.indexOf(publicKey);
|
||||
if (index > -1) {
|
||||
this.publicKeys.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
|
||||
/**
|
||||
* Push Notification actions
|
||||
* SUBSCRIBE - подписаться на push-уведомления
|
||||
* UNSUBSCRIBE - отписаться от push-уведомлений
|
||||
*/
|
||||
export enum PushNotificationAction {
|
||||
SUBSCRIBE = 0,
|
||||
UNSUBSCRIBE = 1,
|
||||
}
|
||||
|
||||
/**
|
||||
* Push Notification packet
|
||||
* ID: 0x10
|
||||
*
|
||||
* Этот пакет отправляется клиентом для подписки на push-уведомления.
|
||||
* Отправлять можно только в том случае, если пользователь уже прошел
|
||||
* рукопожатие и установил соединение.
|
||||
*/
|
||||
export class PacketPushNotification extends Packet {
|
||||
private notificationsToken: string = "";
|
||||
private action: PushNotificationAction = PushNotificationAction.SUBSCRIBE;
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x10;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.notificationsToken = stream.readString();
|
||||
this.action = stream.readInt8();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.notificationsToken);
|
||||
stream.writeInt8(this.action);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getNotificationsToken(): string {
|
||||
return this.notificationsToken;
|
||||
}
|
||||
|
||||
public setNotificationsToken(notificationsToken: string): void {
|
||||
this.notificationsToken = notificationsToken;
|
||||
}
|
||||
|
||||
public getAction(): PushNotificationAction {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public setAction(action: PushNotificationAction): void {
|
||||
this.action = action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketRead extends Packet {
|
||||
|
||||
private privateKey : string = "";
|
||||
private fromPublicKey: string = "";
|
||||
private toPublicKey: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x07;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.privateKey = stream.readString();
|
||||
this.fromPublicKey = stream.readString();
|
||||
this.toPublicKey = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.fromPublicKey);
|
||||
stream.writeString(this.toPublicKey);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setFromPublicKey(fromPublicKey: string): void {
|
||||
this.fromPublicKey = fromPublicKey;
|
||||
}
|
||||
|
||||
public setToPublicKey(toPublicKey: string): void {
|
||||
this.toPublicKey = toPublicKey;
|
||||
}
|
||||
|
||||
public getFromPublicKey(): string {
|
||||
return this.fromPublicKey;
|
||||
}
|
||||
|
||||
public getToPublicKey(): string {
|
||||
return this.toPublicKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketRequestTransport extends Packet {
|
||||
|
||||
private transportServer: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x0F;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.transportServer = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.transportServer);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getTransportServer(): string {
|
||||
return this.transportServer;
|
||||
}
|
||||
|
||||
public setTransportServer(transportServer: string): void {
|
||||
this.transportServer = transportServer;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
|
||||
export class PacketRequestUpdate extends Packet {
|
||||
|
||||
private kernelVersion: string = "";
|
||||
private appVersion: string = "";
|
||||
private arch: string = "";
|
||||
private platform: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0xA;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.kernelVersion = stream.readString();
|
||||
this.appVersion = stream.readString();
|
||||
this.arch = stream.readString();
|
||||
this.platform = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
let stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.kernelVersion);
|
||||
stream.writeString(this.appVersion);
|
||||
stream.writeString(this.arch);
|
||||
stream.writeString(this.platform);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setKernelVersion(version: string): void {
|
||||
this.kernelVersion = version;
|
||||
}
|
||||
|
||||
public getKernelVersion(): string {
|
||||
return this.kernelVersion;
|
||||
}
|
||||
|
||||
public setAppVersion(version: string): void {
|
||||
this.appVersion = version;
|
||||
}
|
||||
|
||||
public getAppVersion(): string {
|
||||
return this.appVersion;
|
||||
}
|
||||
|
||||
public setArch(arch: string): void {
|
||||
this.arch = arch;
|
||||
}
|
||||
|
||||
public getArch(): string {
|
||||
return this.arch;
|
||||
}
|
||||
|
||||
public setPlatform(platform: string): void {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public getPlatform(): string {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum ResultCode {
|
||||
SUCCESS = 0,
|
||||
ERROR = 1,
|
||||
INVALID = 2,
|
||||
USERNAME_TAKEN = 3,
|
||||
}
|
||||
|
||||
export class PacketResult extends Packet {
|
||||
|
||||
private resultCode: ResultCode = 0;
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.resultCode = stream.readInt16();
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeInt16(this.resultCode);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getResultCode(): ResultCode {
|
||||
return this.resultCode;
|
||||
}
|
||||
|
||||
public setResultCode(resultCode: ResultCode): void {
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
import { OnlineState } from "./packet.onlinestate";
|
||||
|
||||
export interface PacketSearchUser {
|
||||
username: string;
|
||||
title: string;
|
||||
publicKey: string;
|
||||
verified: number;
|
||||
online: OnlineState;
|
||||
}
|
||||
|
||||
export class PacketSearch extends Packet {
|
||||
|
||||
private privateKey : string = "";
|
||||
private search : string = "";
|
||||
private users : PacketSearchUser[] = [];
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x03;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.privateKey = stream.readString();
|
||||
this.search = stream.readString();
|
||||
const userCount = stream.readInt16();
|
||||
for (let i = 0; i < userCount; i++) {
|
||||
const username = stream.readString();
|
||||
const title = stream.readString();
|
||||
const publicKey = stream.readString();
|
||||
const verified = stream.readInt8();
|
||||
const online = stream.readInt8();
|
||||
this.users.push({ username, title, publicKey, online, verified });
|
||||
}
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.search);
|
||||
stream.writeInt16(this.users.length);
|
||||
for (const user of this.users) {
|
||||
stream.writeString(user.username);
|
||||
stream.writeString(user.title);
|
||||
stream.writeString(user.publicKey);
|
||||
stream.writeInt8(user.verified);
|
||||
stream.writeInt8(user.online);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public getSearch(): string {
|
||||
return this.search;
|
||||
}
|
||||
|
||||
public setSearch(search: string): void {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
public addUser(searchUser: PacketSearchUser): void {
|
||||
this.users.push(searchUser);
|
||||
}
|
||||
|
||||
public getUsers(): PacketSearchUser[] {
|
||||
return this.users;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketTyping extends Packet {
|
||||
|
||||
private privateKey: string = "";
|
||||
private fromPublicKey: string = "";
|
||||
private toPublicKey: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x0B;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.privateKey = stream.readString();
|
||||
this.fromPublicKey = stream.readString();
|
||||
this.toPublicKey = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.fromPublicKey);
|
||||
stream.writeString(this.toPublicKey);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setFromPublicKey(fromPublicKey: string): void {
|
||||
this.fromPublicKey = fromPublicKey;
|
||||
}
|
||||
|
||||
public setToPublicKey(toPublicKey: string): void {
|
||||
this.toPublicKey = toPublicKey;
|
||||
}
|
||||
|
||||
public getFromPublicKey(): string {
|
||||
return this.fromPublicKey;
|
||||
}
|
||||
|
||||
public getToPublicKey(): string {
|
||||
return this.toPublicKey;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export class PacketUserInfo extends Packet {
|
||||
private privateKey : string = "";
|
||||
private username: string = "";
|
||||
private title: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.username = stream.readString();
|
||||
this.title = stream.readString();
|
||||
this.privateKey = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeString(this.username);
|
||||
stream.writeString(this.title);
|
||||
stream.writeString(this.privateKey);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public getUsername(): string {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public setUsername(username: string): void {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public setTitle(title: string): void {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public setPrivateKey(privateKey: string): void {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public getPrivateKey(): string {
|
||||
return this.privateKey;
|
||||
}
|
||||
}
|
||||
269
app/providers/ProtocolProvider/protocol/protocol.ts
Normal file
269
app/providers/ProtocolProvider/protocol/protocol.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
import { EventEmitter } from "events";
|
||||
import Packet from "./packet";
|
||||
import PacketHandshake, { Device, HandshakeState } from "./packets/packet.handshake";
|
||||
import { PacketMessage } from "./packets/packet.message";
|
||||
import { PacketOnlineState } from "./packets/packet.onlinestate";
|
||||
import { PacketOnlineSubscribe } from "./packets/packet.onlinesubscribe";
|
||||
import { PacketRead } from "./packets/packet.read";
|
||||
import { PacketResult } from "./packets/packet.result";
|
||||
import { PacketSearch } from "./packets/packet.search";
|
||||
import { PacketUserInfo } from "./packets/packet.userinfo";
|
||||
import Stream from "./stream";
|
||||
import { PacketDelivery } from "./packets/packet.delivery";
|
||||
import { PacketRequestUpdate } from "./packets/packet.requestupdate";
|
||||
import { RECONNECTING_INTERVAL } from "@/app/constants";
|
||||
import { PacketTyping } from "./packets/packet.typeing";
|
||||
import { PacketAvatar } from "./packets/packet.avatar";
|
||||
import { PacketKernelUpdate } from "./packets/packet.kernelupdate";
|
||||
import { PacketAppUpdate } from "./packets/packet.appupdate";
|
||||
import { PacketRequestTransport } from "./packets/packet.requesttransport";
|
||||
import { PacketPushNotification } from "./packets/packet.push.notification";
|
||||
import { PacketCreateGroup } from "./packets/packet.create.group";
|
||||
import { PacketGroupInfo } from "./packets/packet.group.info";
|
||||
import { PacketGroupInviteInfo } from "./packets/packet.group.invite.info";
|
||||
import { PacketGroupJoin } from "./packets/packet.group.join";
|
||||
import { PacketGroupLeave } from "./packets/packet.group.leave";
|
||||
import { PacketGroupBan } from "./packets/packet.group.ban";
|
||||
|
||||
export default class Protocol extends EventEmitter {
|
||||
private serverAddress: string;
|
||||
private socket: WebSocket | null = null;
|
||||
private reconnectInterval: number = RECONNECTING_INTERVAL * 1000;
|
||||
private isManuallyClosed: boolean = false;
|
||||
private _supportedPackets: Map<number, Packet> = new Map();
|
||||
private _packetWaiters: Map<number, ((packet: any) => void)[]> = new Map();
|
||||
private _packetQueue: Packet[] = []; // Очередь для пакетов
|
||||
private handshakeExchangeComplete : boolean = false;
|
||||
private heartbeatIntervalTimer : NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(serverAddress: string) {
|
||||
super();
|
||||
this.serverAddress = serverAddress;
|
||||
this.loadAllSupportedPackets();
|
||||
this.connect();
|
||||
|
||||
let _this = this;
|
||||
this.waitPacket(0x00, (packet : PacketHandshake) => {
|
||||
if(packet.getHandshakeState() == HandshakeState.COMPLETED) {
|
||||
console.info('[protocol] %chandshake exchange complete', 'color: #12c456;');
|
||||
_this.emit('handshake_complete');
|
||||
_this.handshakeExchangeComplete = true;
|
||||
_this._flushPacketQueue();
|
||||
this.startHeartbeat(packet.getHeartbeatInterval());
|
||||
}
|
||||
if(packet.getHandshakeState() == HandshakeState.NEED_DEVICE_VERIFICATION) {
|
||||
console.info('[protocol] %chandshake exchange need device verification', 'color: orange;');
|
||||
_this.emit('handshake_need_device_verification');
|
||||
_this._packetQueue = [];
|
||||
this.startHeartbeat(packet.getHeartbeatInterval());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public startHeartbeat(intervalS : number) {
|
||||
const heartbeat = () => {
|
||||
if(this.socket && this.socket.readyState === WebSocket.OPEN){
|
||||
this.socket?.send('heartbeat');
|
||||
}
|
||||
}
|
||||
if(this.heartbeatIntervalTimer){
|
||||
clearInterval(this.heartbeatIntervalTimer);
|
||||
}
|
||||
console.info(`[protocol] %cstarting heartbeat with interval: %c${intervalS} seconds`, 'color: #12c456;', 'color: orange;');
|
||||
heartbeat();
|
||||
this.heartbeatIntervalTimer = setInterval(() => {
|
||||
heartbeat();
|
||||
}, ((intervalS * 1000) / 2));
|
||||
}
|
||||
|
||||
public startHandshakeExchange(publicKey: string, privateKey: string,
|
||||
device: Device
|
||||
) {
|
||||
console.info(
|
||||
`[protocol] %cstarting handshake exchange with server, public key: %c${publicKey}%c, private key hash: %c${privateKey}`,
|
||||
'color: #deadcd;',
|
||||
'color: #12c456;',
|
||||
'color: #deadcd;',
|
||||
'color: #12c456;'
|
||||
);
|
||||
let handshake = new PacketHandshake();
|
||||
handshake.setPublicKey(publicKey);
|
||||
handshake.setPrivateKey(privateKey);
|
||||
handshake.setDevice(device);
|
||||
this.sendPacket(handshake);
|
||||
this.emit('handshake_start');
|
||||
}
|
||||
|
||||
private loadAllSupportedPackets() {
|
||||
this._supportedPackets.set(0x00, new PacketHandshake());
|
||||
this._supportedPackets.set(0x01, new PacketUserInfo());
|
||||
this._supportedPackets.set(0x02, new PacketResult());
|
||||
this._supportedPackets.set(0x03, new PacketSearch());
|
||||
this._supportedPackets.set(0x04, new PacketOnlineSubscribe());
|
||||
this._supportedPackets.set(0x05, new PacketOnlineState());
|
||||
this._supportedPackets.set(0x06, new PacketMessage());
|
||||
this._supportedPackets.set(0x07, new PacketRead());
|
||||
this._supportedPackets.set(0x08, new PacketDelivery());
|
||||
//TODO: 0x09
|
||||
this._supportedPackets.set(0x0A, new PacketRequestUpdate());
|
||||
this._supportedPackets.set(0x0B, new PacketTyping());
|
||||
this._supportedPackets.set(0x0C, new PacketAvatar());
|
||||
this._supportedPackets.set(0x0D, new PacketKernelUpdate());
|
||||
this._supportedPackets.set(0x0E, new PacketAppUpdate());
|
||||
this._supportedPackets.set(0x0F, new PacketRequestTransport());
|
||||
this._supportedPackets.set(0x10, new PacketPushNotification());
|
||||
this._supportedPackets.set(0x11, new PacketCreateGroup());
|
||||
this._supportedPackets.set(0x12, new PacketGroupInfo());
|
||||
this._supportedPackets.set(0x13, new PacketGroupInviteInfo());
|
||||
this._supportedPackets.set(0x14, new PacketGroupJoin());
|
||||
this._supportedPackets.set(0x15, new PacketGroupLeave());
|
||||
this._supportedPackets.set(0x16, new PacketGroupBan());
|
||||
}
|
||||
|
||||
private _findWaiters(packetId: number): ((packet: Packet) => void)[] {
|
||||
if (!this._packetWaiters.has(packetId)) {
|
||||
return [];
|
||||
}
|
||||
return this._packetWaiters.get(packetId)!;
|
||||
}
|
||||
|
||||
private connect() {
|
||||
this.socket = new WebSocket(this.serverAddress);
|
||||
|
||||
this.socket.addEventListener('open', () => {
|
||||
//this.reconnectTryings = 0;
|
||||
this.emit('connect');
|
||||
this._flushPacketQueue(); // Отправляем все пакеты из очереди
|
||||
});
|
||||
|
||||
this.socket.addEventListener('message', async (event: MessageEvent) => {
|
||||
let stream = new Stream();
|
||||
const data = await event.data.arrayBuffer();
|
||||
const numbers = Array.from(new Uint8Array(data));
|
||||
stream.setStream(numbers);
|
||||
const packetId = stream.readInt16();
|
||||
this._supportedPackets.get(packetId);
|
||||
if (!this._supportedPackets.get(packetId)) {
|
||||
console.error(`Unsupported packet ID: ${packetId}`);
|
||||
return;
|
||||
}
|
||||
//чек безопасность
|
||||
const packet = this._supportedPackets.get(packetId)!.clone();
|
||||
packet._receive(stream);
|
||||
const waiters = this._findWaiters(packetId);
|
||||
if (waiters.length === 0) {
|
||||
console.error(`No waiters found for packet ID: ${packetId}`);
|
||||
return;
|
||||
}
|
||||
for (const waiter of waiters) {
|
||||
waiter(packet);
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.addEventListener('close', (e: CloseEvent) => {
|
||||
console.log(`Connection reset by peer`, e.code);
|
||||
this.handshakeExchangeComplete = false;
|
||||
if (!this.isManuallyClosed) {
|
||||
console.log(`Attempting to reconnect...`);
|
||||
this.emit('reconnect');
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
if(this.socket?.readyState == WebSocket.OPEN){
|
||||
return;
|
||||
}
|
||||
}, this.reconnectInterval);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _flushPacketQueue() {
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
for(let i = this._packetQueue.length - 1; i >= 0; i--){
|
||||
let packet : Packet = this._packetQueue[i];
|
||||
if(!this.handshakeExchangeComplete && packet.getPacketId() != 0x00){
|
||||
/**
|
||||
* Если рукопожатие еще не выполнено и текущий пакет для отправки из очереди -
|
||||
* не рукопожатие то пропускаем, отправим в следующее очищение очереди
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
this.sendPacket(packet);
|
||||
this._packetQueue.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async sendPacket(packet: Packet) {
|
||||
if(!this.socket){
|
||||
this.addPacketToQueue(packet);
|
||||
return;
|
||||
}
|
||||
if(this.socket.readyState !== WebSocket.OPEN){
|
||||
this.addPacketToQueue(packet);
|
||||
return;
|
||||
}
|
||||
if(!this.handshakeExchangeComplete && packet.getPacketId() != 0x00){
|
||||
this.addPacketToQueue(packet);
|
||||
return;
|
||||
}
|
||||
const stream = await packet._send();
|
||||
const packetName = packet.constructor.name;
|
||||
|
||||
const pIdHex = packet.getPacketId().toString(16).toLocaleUpperCase();
|
||||
const pIdHexPadded = pIdHex.length === 1 ? '0' + pIdHex : pIdHex;
|
||||
|
||||
console.info(`[protocol] %csending packet: %c${packetName} (ID: 0x${pIdHexPadded})`, 'color: #deadcd;', 'color: orange;');
|
||||
|
||||
/**
|
||||
* Если пакет больше максимально допустимого размера, то разбиваем его на чанки
|
||||
* и отправляем по частям
|
||||
*/
|
||||
this.socket.send(Buffer.from(stream.getStream()));
|
||||
}
|
||||
|
||||
public addPacketToQueue(packet : Packet) {
|
||||
this._packetQueue.push(packet);
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isManuallyClosed = true;
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public unwaitPacket<T>(packet: number, callback: (packet: T) => void) {
|
||||
if (!this._packetWaiters.has(packet)) {
|
||||
return;
|
||||
}
|
||||
const waiters = this._packetWaiters.get(packet)!;
|
||||
const index = waiters.indexOf(callback);
|
||||
if (index !== -1) {
|
||||
waiters.splice(index, 1);
|
||||
}
|
||||
if (waiters.length === 0) {
|
||||
this._packetWaiters.delete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public waitPacket<T>(packet: number, callback: (packet: T) => void) : number {
|
||||
if (!this._packetWaiters.has(packet)) {
|
||||
this._packetWaiters.set(packet, []);
|
||||
}
|
||||
return this._packetWaiters.get(packet)!.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a single packet to be received.
|
||||
* @param packet packet number to wait
|
||||
* @param callback callback to execute once the packet is received
|
||||
*/
|
||||
public waitPacketOnce<T>(packet: number, callback: (packet: T) => void) {
|
||||
let wrapper = (receivedPacket: T) => {
|
||||
callback(receivedPacket);
|
||||
this.unwaitPacket(packet, wrapper);
|
||||
};
|
||||
this.waitPacket(packet, wrapper);
|
||||
}
|
||||
}
|
||||
143
app/providers/ProtocolProvider/protocol/stream.ts
Normal file
143
app/providers/ProtocolProvider/protocol/stream.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
export default class Stream {
|
||||
|
||||
private _stream: number[];
|
||||
private _readPoiner: number = 0;
|
||||
private _writePointer: number = 0;
|
||||
|
||||
constructor(stream : number[] = []) {
|
||||
this._stream = stream;
|
||||
}
|
||||
|
||||
public getStream(): number[] {
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
public setStream(stream: number[]) {
|
||||
this._stream = stream;
|
||||
}
|
||||
|
||||
public writeInt8(value: number) {
|
||||
const negationBit = value < 0 ? 1 : 0;
|
||||
const int8Value = Math.abs(value) & 0xFF;
|
||||
this._stream[this._writePointer >> 3] |= negationBit << (7 - (this._writePointer & 7));
|
||||
this._writePointer++;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const bit = (int8Value >> (7 - i)) & 1;
|
||||
this._stream[this._writePointer >> 3] |= bit << (7 - (this._writePointer & 7));
|
||||
this._writePointer++;
|
||||
}
|
||||
}
|
||||
|
||||
public readInt8(): number {
|
||||
let value = 0;
|
||||
const negationBit = (this._stream[this._readPoiner >> 3] >> (7 - (this._readPoiner & 7))) & 1;
|
||||
this._readPoiner++;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const bit = (this._stream[this._readPoiner >> 3] >> (7 - (this._readPoiner & 7))) & 1;
|
||||
value |= bit << (7 - i);
|
||||
this._readPoiner++;
|
||||
}
|
||||
return negationBit ? -value : value;
|
||||
}
|
||||
|
||||
public writeBit(value: number) {
|
||||
const bit = value & 1;
|
||||
this._stream[this._writePointer >> 3] |= bit << (7 - (this._writePointer & 7));
|
||||
this._writePointer++;
|
||||
}
|
||||
|
||||
public readBit(): number {
|
||||
const bit = (this._stream[this._readPoiner >> 3] >> (7 - (this._readPoiner & 7))) & 1;
|
||||
this._readPoiner++;
|
||||
return bit;
|
||||
}
|
||||
|
||||
public writeBoolean(value: boolean) {
|
||||
this.writeBit(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public readBoolean(): boolean {
|
||||
return this.readBit() === 1;
|
||||
}
|
||||
|
||||
public writeInt16(value: number) {
|
||||
this.writeInt8(value >> 8);
|
||||
this.writeInt8(value & 0xFF);
|
||||
}
|
||||
|
||||
public readInt16(): number {
|
||||
const value = this.readInt8() << 8;
|
||||
return value | this.readInt8();
|
||||
}
|
||||
|
||||
public writeInt32(value: number) {
|
||||
this.writeInt16(value >> 16);
|
||||
this.writeInt16(value & 0xFFFF);
|
||||
}
|
||||
|
||||
public readInt32(): number {
|
||||
const value = this.readInt16() << 16;
|
||||
return value | this.readInt16();
|
||||
}
|
||||
|
||||
public writeFloat32(value: number) {
|
||||
const buffer = new ArrayBuffer(4);
|
||||
new DataView(buffer).setFloat32(0, value, true);
|
||||
const float32Value = new Uint32Array(buffer)[0];
|
||||
this.writeInt32(float32Value);
|
||||
}
|
||||
|
||||
public readFloat32(): number {
|
||||
const float32Value = this.readInt32();
|
||||
const buffer = new ArrayBuffer(4);
|
||||
new Uint32Array(buffer)[0] = float32Value;
|
||||
return new DataView(buffer).getFloat32(0, true);
|
||||
}
|
||||
|
||||
public writeInt64(value: number) {
|
||||
const high = Math.floor(value / 0x100000000);
|
||||
const low = value >>> 0;
|
||||
this.writeInt32(high);
|
||||
this.writeInt32(low);
|
||||
}
|
||||
|
||||
public readInt64(): number {
|
||||
const high = this.readInt32();
|
||||
const low = this.readInt32() >>> 0;
|
||||
return high * 0x100000000 + low;
|
||||
}
|
||||
|
||||
public writeString(value: string) {
|
||||
let length = value.length;
|
||||
this.writeInt32(length);
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
this.writeInt16(value.charCodeAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
public readString(): string {
|
||||
let length = this.readInt32();
|
||||
let value = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
value += String.fromCharCode(this.readInt16());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public writeBytes(value: number[]) {
|
||||
this.writeInt32(value.length);
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
this.writeInt8(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public readBytes(): number[] {
|
||||
let length = this.readInt32();
|
||||
let value : any = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
value.push(this.readInt8());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user