Add network packet classes and enums for messaging functionality
- Introduced `AttachmentType` enum to define various attachment types. - Added `DeliveryStatus` enum to represent message delivery statuses. - Created `MessageAttachment` data class to encapsulate message attachments. - Implemented `OnlineState` enum to manage user online statuses. - Developed various packet classes: `Packet`, `PacketHandshake`, `PacketResult`, `PacketSearch`, `PacketUserInfo`, `PacketOnlineState`, `PacketOnlineSubscribe`, `PacketMessage`, `PacketRead`, `PacketDelivery`, `PacketTyping`, `PacketChunk`, `PacketPushNotification`, `PacketRequestTransport`, and `PacketPushToken` for handling different messaging operations. - Removed the old `Packets.kt` file to streamline the codebase. - Added `SearchUser` data class for user search results.
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Типы вложений
|
||||||
|
*/
|
||||||
|
enum class AttachmentType(val value: Int) {
|
||||||
|
IMAGE(0), // Изображение
|
||||||
|
MESSAGES(1), // Reply (цитата сообщения)
|
||||||
|
FILE(2), // Файл
|
||||||
|
AVATAR(3); // Аватар пользователя
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: IMAGE
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Статус доставки сообщения
|
||||||
|
*/
|
||||||
|
enum class DeliveryStatus(val value: Int) {
|
||||||
|
WAITING(0), // Ожидает отправки
|
||||||
|
DELIVERED(1), // Доставлено
|
||||||
|
ERROR(2), // Ошибка
|
||||||
|
READ(3); // Прочитано
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: WAITING
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вложение к сообщению
|
||||||
|
*/
|
||||||
|
data class MessageAttachment(
|
||||||
|
val id: String,
|
||||||
|
val blob: String, // Base64 данные или пусто для CDN
|
||||||
|
val type: AttachmentType,
|
||||||
|
val preview: String = "", // Метаданные: "UUID::metadata" или "filesize::filename"
|
||||||
|
val width: Int = 0,
|
||||||
|
val height: Int = 0,
|
||||||
|
val localUri: String = "" // 🚀 Локальный URI для мгновенного отображения (optimistic UI)
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Online State enum
|
||||||
|
*/
|
||||||
|
enum class OnlineState(val value: Int) {
|
||||||
|
ONLINE(0),
|
||||||
|
OFFLINE(1);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromBoolean(isOnline: Boolean) = if (isOnline) ONLINE else OFFLINE
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/src/main/java/com/rosetta/messenger/network/Packet.kt
Normal file
10
app/src/main/java/com/rosetta/messenger/network/Packet.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all protocol packets
|
||||||
|
*/
|
||||||
|
abstract class Packet {
|
||||||
|
abstract fun getPacketId(): Int
|
||||||
|
abstract fun receive(stream: Stream)
|
||||||
|
abstract fun send(): Stream
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunk packet (ID: 0x09)
|
||||||
|
* Для разбиения больших пакетов на части (как в Desktop)
|
||||||
|
* ВАЖНО: chunkIndex и totalChunks - Int16, не Int32!
|
||||||
|
*/
|
||||||
|
class PacketChunk : Packet() {
|
||||||
|
var chunkId: String = ""
|
||||||
|
var chunkIndex: Int = 0
|
||||||
|
var totalChunks: Int = 0
|
||||||
|
var data: ByteArray = ByteArray(0)
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x09
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
// В Desktop: readInt16 для index и total
|
||||||
|
chunkIndex = stream.readInt16()
|
||||||
|
totalChunks = stream.readInt16()
|
||||||
|
chunkId = stream.readString()
|
||||||
|
data = stream.readBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
// В Desktop: writeInt16 для index и total
|
||||||
|
stream.writeInt16(chunkIndex)
|
||||||
|
stream.writeInt16(totalChunks)
|
||||||
|
stream.writeString(chunkId)
|
||||||
|
stream.writeBytes(data)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delivery packet (ID: 0x08)
|
||||||
|
* Уведомление о доставке сообщения
|
||||||
|
* Порядок полей как в React Native: toPublicKey, messageId
|
||||||
|
*/
|
||||||
|
class PacketDelivery : Packet() {
|
||||||
|
var messageId: String = ""
|
||||||
|
var toPublicKey: String = ""
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x08
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
// React Native читает: toPublicKey, messageId
|
||||||
|
toPublicKey = stream.readString()
|
||||||
|
messageId = stream.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
// React Native пишет: toPublicKey, messageId
|
||||||
|
stream.writeString(toPublicKey)
|
||||||
|
stream.writeString(messageId)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handshake packet (ID: 0x00)
|
||||||
|
* First packet sent by client to authenticate with the server
|
||||||
|
*/
|
||||||
|
class PacketHandshake : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var publicKey: String = ""
|
||||||
|
var protocolVersion: Int = 1
|
||||||
|
var heartbeatInterval: Int = 15
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x00
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
privateKey = stream.readString()
|
||||||
|
publicKey = stream.readString()
|
||||||
|
protocolVersion = stream.readInt8()
|
||||||
|
heartbeatInterval = stream.readInt8()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeString(publicKey)
|
||||||
|
stream.writeInt8(protocolVersion)
|
||||||
|
stream.writeInt8(heartbeatInterval)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message packet (ID: 0x06)
|
||||||
|
* Отправка и получение сообщений
|
||||||
|
*/
|
||||||
|
class PacketMessage : Packet() {
|
||||||
|
var fromPublicKey: String = ""
|
||||||
|
var toPublicKey: String = ""
|
||||||
|
var content: String = "" // Зашифрованный текст
|
||||||
|
var chachaKey: String = "" // RSA зашифрованный ключ
|
||||||
|
var timestamp: Long = 0
|
||||||
|
var privateKey: String = "" // Hash приватного ключа (для авторизации)
|
||||||
|
var messageId: String = ""
|
||||||
|
var attachments: List<MessageAttachment> = emptyList()
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x06
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
fromPublicKey = stream.readString()
|
||||||
|
toPublicKey = stream.readString()
|
||||||
|
content = stream.readString()
|
||||||
|
chachaKey = stream.readString()
|
||||||
|
timestamp = stream.readInt64()
|
||||||
|
privateKey = stream.readString()
|
||||||
|
messageId = stream.readString()
|
||||||
|
|
||||||
|
val attachmentCount = stream.readInt8()
|
||||||
|
val attachmentsList = mutableListOf<MessageAttachment>()
|
||||||
|
for (i in 0 until attachmentCount) {
|
||||||
|
attachmentsList.add(MessageAttachment(
|
||||||
|
id = stream.readString(),
|
||||||
|
preview = stream.readString(),
|
||||||
|
blob = stream.readString(),
|
||||||
|
type = AttachmentType.fromInt(stream.readInt8())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
attachments = attachmentsList
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(fromPublicKey)
|
||||||
|
stream.writeString(toPublicKey)
|
||||||
|
stream.writeString(content)
|
||||||
|
stream.writeString(chachaKey)
|
||||||
|
stream.writeInt64(timestamp)
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeString(messageId)
|
||||||
|
stream.writeInt8(attachments.size)
|
||||||
|
|
||||||
|
for (attachment in attachments) {
|
||||||
|
stream.writeString(attachment.id)
|
||||||
|
stream.writeString(attachment.preview)
|
||||||
|
stream.writeString(attachment.blob)
|
||||||
|
stream.writeInt8(attachment.type.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key with online state
|
||||||
|
*/
|
||||||
|
data class PublicKeyOnlineState(
|
||||||
|
val publicKey: String,
|
||||||
|
val state: OnlineState
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Online State packet (ID: 0x05)
|
||||||
|
* Notify about user online status
|
||||||
|
* Формат как в React Native: массив {publicKey, state}
|
||||||
|
*/
|
||||||
|
class PacketOnlineState : Packet() {
|
||||||
|
var publicKeysState: MutableList<PublicKeyOnlineState> = mutableListOf()
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x05
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
val count = stream.readInt8()
|
||||||
|
publicKeysState.clear()
|
||||||
|
repeat(count) {
|
||||||
|
val publicKey = stream.readString()
|
||||||
|
val isOnline = stream.readBoolean()
|
||||||
|
publicKeysState.add(PublicKeyOnlineState(publicKey, OnlineState.fromBoolean(isOnline)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeInt8(publicKeysState.size)
|
||||||
|
publicKeysState.forEach { item ->
|
||||||
|
stream.writeString(item.publicKey)
|
||||||
|
stream.writeBoolean(item.state == OnlineState.ONLINE)
|
||||||
|
}
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Online Subscribe packet (ID: 0x04)
|
||||||
|
* Subscribe to user online status updates
|
||||||
|
* Формат как в React Native: privateKey + array of publicKeys
|
||||||
|
*/
|
||||||
|
class PacketOnlineSubscribe : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var publicKeys: MutableList<String> = mutableListOf()
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x04
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
privateKey = stream.readString()
|
||||||
|
val keysCount = stream.readInt16()
|
||||||
|
publicKeys.clear()
|
||||||
|
repeat(keysCount) {
|
||||||
|
publicKeys.add(stream.readString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeInt16(publicKeys.size)
|
||||||
|
publicKeys.forEach { key ->
|
||||||
|
stream.writeString(key)
|
||||||
|
}
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addPublicKey(publicKey: String) {
|
||||||
|
publicKeys.add(publicKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push Notification Action
|
||||||
|
*/
|
||||||
|
enum class PushNotificationAction(val value: Int) {
|
||||||
|
SUBSCRIBE(0),
|
||||||
|
UNSUBSCRIBE(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push Notification packet (ID: 0x10)
|
||||||
|
* Отправка FCM/APNS токена на сервер для push-уведомлений (новый формат)
|
||||||
|
* Совместим с React Native версией
|
||||||
|
*/
|
||||||
|
class PacketPushNotification : Packet() {
|
||||||
|
var notificationsToken: String = ""
|
||||||
|
var action: PushNotificationAction = PushNotificationAction.SUBSCRIBE
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x10
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
notificationsToken = stream.readString()
|
||||||
|
action = when (stream.readInt8()) {
|
||||||
|
1 -> PushNotificationAction.UNSUBSCRIBE
|
||||||
|
else -> PushNotificationAction.SUBSCRIBE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(notificationsToken)
|
||||||
|
stream.writeInt8(action.value)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push Token packet (ID: 0x0A) - DEPRECATED
|
||||||
|
* Старый формат, заменен на PacketPushNotification (0x10)
|
||||||
|
*/
|
||||||
|
class PacketPushToken : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var publicKey: String = ""
|
||||||
|
var pushToken: String = ""
|
||||||
|
var platform: String = "android" // "android" или "ios"
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x0A
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
privateKey = stream.readString()
|
||||||
|
publicKey = stream.readString()
|
||||||
|
pushToken = stream.readString()
|
||||||
|
platform = stream.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeString(publicKey)
|
||||||
|
stream.writeString(pushToken)
|
||||||
|
stream.writeString(platform)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read packet (ID: 0x07)
|
||||||
|
* Уведомление о прочтении сообщения
|
||||||
|
* Порядок полей как в Desktop: privateKey, fromPublicKey, toPublicKey
|
||||||
|
*/
|
||||||
|
class PacketRead : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var fromPublicKey: String = ""
|
||||||
|
var toPublicKey: String = ""
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x07
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
// Desktop: privateKey, fromPublicKey, toPublicKey
|
||||||
|
privateKey = stream.readString()
|
||||||
|
fromPublicKey = stream.readString()
|
||||||
|
toPublicKey = stream.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
// Desktop: privateKey, fromPublicKey, toPublicKey
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeString(fromPublicKey)
|
||||||
|
stream.writeString(toPublicKey)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request Transport packet (ID: 0x0F)
|
||||||
|
* Запрос адреса транспортного сервера для загрузки/скачивания файлов
|
||||||
|
*/
|
||||||
|
class PacketRequestTransport : Packet() {
|
||||||
|
var transportServer: String = ""
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x0F
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
transportServer = stream.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(transportServer)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result packet (ID: 0x02)
|
||||||
|
* Server response for various operations
|
||||||
|
* Desktop uses: readInt16() for resultCode only
|
||||||
|
*/
|
||||||
|
class PacketResult : Packet() {
|
||||||
|
var resultCode: Int = 0
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x02
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
// Desktop: this.resultCode = stream.readInt16();
|
||||||
|
resultCode = stream.readInt16()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeInt16(resultCode)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search packet (ID: 0x03)
|
||||||
|
* Search for users by username or public key
|
||||||
|
*/
|
||||||
|
class PacketSearch : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var search: String = ""
|
||||||
|
var users: List<SearchUser> = emptyList()
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x03
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
privateKey = stream.readString()
|
||||||
|
search = stream.readString()
|
||||||
|
val userCount = stream.readInt16() // Int16, not Int32!
|
||||||
|
val usersList = mutableListOf<SearchUser>()
|
||||||
|
for (i in 0 until userCount) {
|
||||||
|
// Order: username, title, publicKey, verified, online (matching React Native)
|
||||||
|
val user = SearchUser(
|
||||||
|
username = stream.readString(),
|
||||||
|
title = stream.readString(),
|
||||||
|
publicKey = stream.readString(),
|
||||||
|
verified = stream.readInt8(),
|
||||||
|
online = stream.readInt8()
|
||||||
|
)
|
||||||
|
usersList.add(user)
|
||||||
|
}
|
||||||
|
users = usersList
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeString(search)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typing packet (ID: 0x0B)
|
||||||
|
* Порядок полей как в React Native: privateKey, fromPublicKey, toPublicKey
|
||||||
|
*/
|
||||||
|
class PacketTyping : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var fromPublicKey: String = ""
|
||||||
|
var toPublicKey: String = ""
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x0B
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
privateKey = stream.readString()
|
||||||
|
fromPublicKey = stream.readString()
|
||||||
|
toPublicKey = stream.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
stream.writeString(fromPublicKey)
|
||||||
|
stream.writeString(toPublicKey)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Info packet (ID: 0x01)
|
||||||
|
* Get/Set user information
|
||||||
|
* Protocol order: username, title, privateKey (matching TypeScript version)
|
||||||
|
*/
|
||||||
|
class PacketUserInfo : Packet() {
|
||||||
|
var privateKey: String = ""
|
||||||
|
var username: String = ""
|
||||||
|
var title: String = ""
|
||||||
|
|
||||||
|
override fun getPacketId(): Int = 0x01
|
||||||
|
|
||||||
|
override fun receive(stream: Stream) {
|
||||||
|
username = stream.readString()
|
||||||
|
title = stream.readString()
|
||||||
|
privateKey = stream.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(): Stream {
|
||||||
|
val stream = Stream()
|
||||||
|
stream.writeInt16(getPacketId())
|
||||||
|
stream.writeString(username)
|
||||||
|
stream.writeString(title)
|
||||||
|
stream.writeString(privateKey)
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,540 +0,0 @@
|
|||||||
package com.rosetta.messenger.network
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all protocol packets
|
|
||||||
*/
|
|
||||||
abstract class Packet {
|
|
||||||
abstract fun getPacketId(): Int
|
|
||||||
abstract fun receive(stream: Stream)
|
|
||||||
abstract fun send(): Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handshake packet (ID: 0x00)
|
|
||||||
* First packet sent by client to authenticate with the server
|
|
||||||
*/
|
|
||||||
class PacketHandshake : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var publicKey: String = ""
|
|
||||||
var protocolVersion: Int = 1
|
|
||||||
var heartbeatInterval: Int = 15
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x00
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
privateKey = stream.readString()
|
|
||||||
publicKey = stream.readString()
|
|
||||||
protocolVersion = stream.readInt8()
|
|
||||||
heartbeatInterval = stream.readInt8()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeString(publicKey)
|
|
||||||
stream.writeInt8(protocolVersion)
|
|
||||||
stream.writeInt8(heartbeatInterval)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Result packet (ID: 0x02)
|
|
||||||
* Server response for various operations
|
|
||||||
* Desktop uses: readInt16() for resultCode only
|
|
||||||
*/
|
|
||||||
class PacketResult : Packet() {
|
|
||||||
var resultCode: Int = 0
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x02
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
// Desktop: this.resultCode = stream.readInt16();
|
|
||||||
resultCode = stream.readInt16()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeInt16(resultCode)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search packet (ID: 0x03)
|
|
||||||
* Search for users by username or public key
|
|
||||||
*/
|
|
||||||
class PacketSearch : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var search: String = ""
|
|
||||||
var users: List<SearchUser> = emptyList()
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x03
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
privateKey = stream.readString()
|
|
||||||
search = stream.readString()
|
|
||||||
val userCount = stream.readInt16() // Int16, not Int32!
|
|
||||||
val usersList = mutableListOf<SearchUser>()
|
|
||||||
for (i in 0 until userCount) {
|
|
||||||
// Order: username, title, publicKey, verified, online (matching React Native)
|
|
||||||
val user = SearchUser(
|
|
||||||
username = stream.readString(),
|
|
||||||
title = stream.readString(),
|
|
||||||
publicKey = stream.readString(),
|
|
||||||
verified = stream.readInt8(),
|
|
||||||
online = stream.readInt8()
|
|
||||||
)
|
|
||||||
usersList.add(user)
|
|
||||||
}
|
|
||||||
users = usersList
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeString(search)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class SearchUser(
|
|
||||||
val publicKey: String,
|
|
||||||
val title: String,
|
|
||||||
val username: String,
|
|
||||||
val verified: Int,
|
|
||||||
val online: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User Info packet (ID: 0x01)
|
|
||||||
* Get/Set user information
|
|
||||||
* Protocol order: username, title, privateKey (matching TypeScript version)
|
|
||||||
*/
|
|
||||||
class PacketUserInfo : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var username: String = ""
|
|
||||||
var title: String = ""
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x01
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
username = stream.readString()
|
|
||||||
title = stream.readString()
|
|
||||||
privateKey = stream.readString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(username)
|
|
||||||
stream.writeString(title)
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Online State enum
|
|
||||||
*/
|
|
||||||
enum class OnlineState(val value: Int) {
|
|
||||||
ONLINE(0),
|
|
||||||
OFFLINE(1);
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromBoolean(isOnline: Boolean) = if (isOnline) ONLINE else OFFLINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public key with online state
|
|
||||||
*/
|
|
||||||
data class PublicKeyOnlineState(
|
|
||||||
val publicKey: String,
|
|
||||||
val state: OnlineState
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Online State packet (ID: 0x05)
|
|
||||||
* Notify about user online status
|
|
||||||
* Формат как в React Native: массив {publicKey, state}
|
|
||||||
*/
|
|
||||||
class PacketOnlineState : Packet() {
|
|
||||||
var publicKeysState: MutableList<PublicKeyOnlineState> = mutableListOf()
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x05
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
val count = stream.readInt8()
|
|
||||||
publicKeysState.clear()
|
|
||||||
repeat(count) {
|
|
||||||
val publicKey = stream.readString()
|
|
||||||
val isOnline = stream.readBoolean()
|
|
||||||
publicKeysState.add(PublicKeyOnlineState(publicKey, OnlineState.fromBoolean(isOnline)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeInt8(publicKeysState.size)
|
|
||||||
publicKeysState.forEach { item ->
|
|
||||||
stream.writeString(item.publicKey)
|
|
||||||
stream.writeBoolean(item.state == OnlineState.ONLINE)
|
|
||||||
}
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Online Subscribe packet (ID: 0x04)
|
|
||||||
* Subscribe to user online status updates
|
|
||||||
* Формат как в React Native: privateKey + array of publicKeys
|
|
||||||
*/
|
|
||||||
class PacketOnlineSubscribe : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var publicKeys: MutableList<String> = mutableListOf()
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x04
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
privateKey = stream.readString()
|
|
||||||
val keysCount = stream.readInt16()
|
|
||||||
publicKeys.clear()
|
|
||||||
repeat(keysCount) {
|
|
||||||
publicKeys.add(stream.readString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeInt16(publicKeys.size)
|
|
||||||
publicKeys.forEach { key ->
|
|
||||||
stream.writeString(key)
|
|
||||||
}
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addPublicKey(publicKey: String) {
|
|
||||||
publicKeys.add(publicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// MESSAGE PACKETS - Как в React Native версии
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Типы вложений
|
|
||||||
*/
|
|
||||||
enum class AttachmentType(val value: Int) {
|
|
||||||
IMAGE(0), // Изображение
|
|
||||||
MESSAGES(1), // Reply (цитата сообщения)
|
|
||||||
FILE(2), // Файл
|
|
||||||
AVATAR(3); // Аватар пользователя
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: IMAGE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Статус доставки сообщения
|
|
||||||
*/
|
|
||||||
enum class DeliveryStatus(val value: Int) {
|
|
||||||
WAITING(0), // Ожидает отправки
|
|
||||||
DELIVERED(1), // Доставлено
|
|
||||||
ERROR(2), // Ошибка
|
|
||||||
READ(3); // Прочитано
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: WAITING
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Вложение к сообщению
|
|
||||||
*/
|
|
||||||
data class MessageAttachment(
|
|
||||||
val id: String,
|
|
||||||
val blob: String, // Base64 данные или пусто для CDN
|
|
||||||
val type: AttachmentType,
|
|
||||||
val preview: String = "", // Метаданные: "UUID::metadata" или "filesize::filename"
|
|
||||||
val width: Int = 0,
|
|
||||||
val height: Int = 0,
|
|
||||||
val localUri: String = "" // 🚀 Локальный URI для мгновенного отображения (optimistic UI)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message packet (ID: 0x06)
|
|
||||||
* Отправка и получение сообщений
|
|
||||||
*/
|
|
||||||
class PacketMessage : Packet() {
|
|
||||||
var fromPublicKey: String = ""
|
|
||||||
var toPublicKey: String = ""
|
|
||||||
var content: String = "" // Зашифрованный текст
|
|
||||||
var chachaKey: String = "" // RSA зашифрованный ключ
|
|
||||||
var timestamp: Long = 0
|
|
||||||
var privateKey: String = "" // Hash приватного ключа (для авторизации)
|
|
||||||
var messageId: String = ""
|
|
||||||
var attachments: List<MessageAttachment> = emptyList()
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x06
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
fromPublicKey = stream.readString()
|
|
||||||
toPublicKey = stream.readString()
|
|
||||||
content = stream.readString()
|
|
||||||
chachaKey = stream.readString()
|
|
||||||
timestamp = stream.readInt64()
|
|
||||||
privateKey = stream.readString()
|
|
||||||
messageId = stream.readString()
|
|
||||||
|
|
||||||
val attachmentCount = stream.readInt8()
|
|
||||||
val attachmentsList = mutableListOf<MessageAttachment>()
|
|
||||||
for (i in 0 until attachmentCount) {
|
|
||||||
attachmentsList.add(MessageAttachment(
|
|
||||||
id = stream.readString(),
|
|
||||||
preview = stream.readString(),
|
|
||||||
blob = stream.readString(),
|
|
||||||
type = AttachmentType.fromInt(stream.readInt8())
|
|
||||||
))
|
|
||||||
}
|
|
||||||
attachments = attachmentsList
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(fromPublicKey)
|
|
||||||
stream.writeString(toPublicKey)
|
|
||||||
stream.writeString(content)
|
|
||||||
stream.writeString(chachaKey)
|
|
||||||
stream.writeInt64(timestamp)
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeString(messageId)
|
|
||||||
stream.writeInt8(attachments.size)
|
|
||||||
|
|
||||||
for (attachment in attachments) {
|
|
||||||
stream.writeString(attachment.id)
|
|
||||||
stream.writeString(attachment.preview)
|
|
||||||
stream.writeString(attachment.blob)
|
|
||||||
stream.writeInt8(attachment.type.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read packet (ID: 0x07)
|
|
||||||
* Уведомление о прочтении сообщения
|
|
||||||
* Порядок полей как в Desktop: privateKey, fromPublicKey, toPublicKey
|
|
||||||
*/
|
|
||||||
class PacketRead : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var fromPublicKey: String = ""
|
|
||||||
var toPublicKey: String = ""
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x07
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
// Desktop: privateKey, fromPublicKey, toPublicKey
|
|
||||||
privateKey = stream.readString()
|
|
||||||
fromPublicKey = stream.readString()
|
|
||||||
toPublicKey = stream.readString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
// Desktop: privateKey, fromPublicKey, toPublicKey
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeString(fromPublicKey)
|
|
||||||
stream.writeString(toPublicKey)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delivery packet (ID: 0x08)
|
|
||||||
* Уведомление о доставке сообщения
|
|
||||||
* Порядок полей как в React Native: toPublicKey, messageId
|
|
||||||
*/
|
|
||||||
class PacketDelivery : Packet() {
|
|
||||||
var messageId: String = ""
|
|
||||||
var toPublicKey: String = ""
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x08
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
// React Native читает: toPublicKey, messageId
|
|
||||||
toPublicKey = stream.readString()
|
|
||||||
messageId = stream.readString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
// React Native пишет: toPublicKey, messageId
|
|
||||||
stream.writeString(toPublicKey)
|
|
||||||
stream.writeString(messageId)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typing packet (ID: 0x0B)
|
|
||||||
* Уведомление "печатает..."
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Typing packet (ID: 0x0B)
|
|
||||||
* Порядок полей как в React Native: privateKey, fromPublicKey, toPublicKey
|
|
||||||
*/
|
|
||||||
class PacketTyping : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var fromPublicKey: String = ""
|
|
||||||
var toPublicKey: String = ""
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x0B
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
privateKey = stream.readString()
|
|
||||||
fromPublicKey = stream.readString()
|
|
||||||
toPublicKey = stream.readString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeString(fromPublicKey)
|
|
||||||
stream.writeString(toPublicKey)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chunk packet (ID: 0x09)
|
|
||||||
* Для разбиения больших пакетов на части (как в Desktop)
|
|
||||||
* ВАЖНО: chunkIndex и totalChunks - Int16, не Int32!
|
|
||||||
*/
|
|
||||||
class PacketChunk : Packet() {
|
|
||||||
var chunkId: String = ""
|
|
||||||
var chunkIndex: Int = 0
|
|
||||||
var totalChunks: Int = 0
|
|
||||||
var data: ByteArray = ByteArray(0)
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x09
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
// В Desktop: readInt16 для index и total
|
|
||||||
chunkIndex = stream.readInt16()
|
|
||||||
totalChunks = stream.readInt16()
|
|
||||||
chunkId = stream.readString()
|
|
||||||
data = stream.readBytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
// В Desktop: writeInt16 для index и total
|
|
||||||
stream.writeInt16(chunkIndex)
|
|
||||||
stream.writeInt16(totalChunks)
|
|
||||||
stream.writeString(chunkId)
|
|
||||||
stream.writeBytes(data)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push Token packet (ID: 0x0A) - DEPRECATED
|
|
||||||
* Старый формат, заменен на PacketPushNotification (0x10)
|
|
||||||
*/
|
|
||||||
class PacketPushToken : Packet() {
|
|
||||||
var privateKey: String = ""
|
|
||||||
var publicKey: String = ""
|
|
||||||
var pushToken: String = ""
|
|
||||||
var platform: String = "android" // "android" или "ios"
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x0A
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
privateKey = stream.readString()
|
|
||||||
publicKey = stream.readString()
|
|
||||||
pushToken = stream.readString()
|
|
||||||
platform = stream.readString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(privateKey)
|
|
||||||
stream.writeString(publicKey)
|
|
||||||
stream.writeString(pushToken)
|
|
||||||
stream.writeString(platform)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push Notification Action
|
|
||||||
*/
|
|
||||||
enum class PushNotificationAction(val value: Int) {
|
|
||||||
SUBSCRIBE(0),
|
|
||||||
UNSUBSCRIBE(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push Notification packet (ID: 0x10)
|
|
||||||
* Отправка FCM/APNS токена на сервер для push-уведомлений (новый формат)
|
|
||||||
* Совместим с React Native версией
|
|
||||||
*/
|
|
||||||
class PacketPushNotification : Packet() {
|
|
||||||
var notificationsToken: String = ""
|
|
||||||
var action: PushNotificationAction = PushNotificationAction.SUBSCRIBE
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x10
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
notificationsToken = stream.readString()
|
|
||||||
action = when (stream.readInt8()) {
|
|
||||||
1 -> PushNotificationAction.UNSUBSCRIBE
|
|
||||||
else -> PushNotificationAction.SUBSCRIBE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(notificationsToken)
|
|
||||||
stream.writeInt8(action.value)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request Transport packet (ID: 0x0F)
|
|
||||||
* Запрос адреса транспортного сервера для загрузки/скачивания файлов
|
|
||||||
*/
|
|
||||||
class PacketRequestTransport : Packet() {
|
|
||||||
var transportServer: String = ""
|
|
||||||
|
|
||||||
override fun getPacketId(): Int = 0x0F
|
|
||||||
|
|
||||||
override fun receive(stream: Stream) {
|
|
||||||
transportServer = stream.readString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun send(): Stream {
|
|
||||||
val stream = Stream()
|
|
||||||
stream.writeInt16(getPacketId())
|
|
||||||
stream.writeString(transportServer)
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.rosetta.messenger.network
|
||||||
|
|
||||||
|
data class SearchUser(
|
||||||
|
val publicKey: String,
|
||||||
|
val title: String,
|
||||||
|
val username: String,
|
||||||
|
val verified: Int,
|
||||||
|
val online: Int
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user