From 0981cd3d4b91cf869fa03b94f172edb9d7feea11 Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Wed, 11 Feb 2026 11:48:36 +0200 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D0=B0=2016=20?= =?UTF-8?q?=D1=81=20=D0=BF=D1=83=D1=88=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/rosetta/im/Boot.java | 18 ++++++ src/main/java/com/rosetta/im/Main.java | 2 +- .../com/rosetta/im/database/Repository.java | 1 + .../executors/Executor16PushNotification.java | 47 ++++++++++++++ .../im/packet/Packet16PushNotification.java | 63 +++++++++++++++++++ .../runtime/NetworkNotificationAction.java | 34 ++++++++++ .../im/service/services/UserService.java | 30 +++++++++ src/main/java/io/orprotocol/Server.java | 1 + .../java/io/orprotocol/client/Client.java | 1 + 9 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/rosetta/im/executors/Executor16PushNotification.java create mode 100644 src/main/java/com/rosetta/im/packet/Packet16PushNotification.java create mode 100644 src/main/java/com/rosetta/im/packet/runtime/NetworkNotificationAction.java diff --git a/src/main/java/com/rosetta/im/Boot.java b/src/main/java/com/rosetta/im/Boot.java index 2a3770f..467a9ee 100644 --- a/src/main/java/com/rosetta/im/Boot.java +++ b/src/main/java/com/rosetta/im/Boot.java @@ -9,6 +9,7 @@ import com.rosetta.im.executors.Executor0Handshake; import com.rosetta.im.executors.Executor10RequestUpdate; import com.rosetta.im.executors.Executor11Typeing; import com.rosetta.im.executors.Executor15RequestTransport; +import com.rosetta.im.executors.Executor16PushNotification; import com.rosetta.im.executors.Executor1UserInfo; import com.rosetta.im.executors.Executor24DeviceResolve; import com.rosetta.im.executors.Executor3Search; @@ -27,6 +28,7 @@ import com.rosetta.im.packet.Packet0Handshake; import com.rosetta.im.packet.Packet10RequestUpdate; import com.rosetta.im.packet.Packet11Typeing; import com.rosetta.im.packet.Packet15RequestTransport; +import com.rosetta.im.packet.Packet16PushNotification; import com.rosetta.im.packet.Packet1UserInfo; import com.rosetta.im.packet.Packet23DeviceList; import com.rosetta.im.packet.Packet24DeviceResolve; @@ -62,6 +64,17 @@ public class Boot { private Configuration configuration; private ServerConfiguration serverConfiguration; + /** + * Конструктор по умолчанию, использует порт 3000 для сервера + */ + public Boot() { + this(3000); + } + + /** + * Инициализатор приложения + * @param port Порт, на котором будет работать сервер. Если не указан, то будет использован порт 3000 + */ public Boot(int port) { this.packetManager = new PacketManager(); this.eventManager = new EventManager(); @@ -149,7 +162,11 @@ public class Boot { this.packetManager.registerPacket(9, Packet9DeviceNew.class); this.packetManager.registerPacket(10, Packet10RequestUpdate.class); this.packetManager.registerPacket(11, Packet11Typeing.class); + //RESERVED 12 PACKET AVATAR (unused) + //RESERVED 13 PACKET KERNEL UPDATE (unused) + //RESERVED 14 PACKET APP UPDATE (unused) this.packetManager.registerPacket(15, Packet15RequestTransport.class); + this.packetManager.registerPacket(16, Packet16PushNotification.class); this.packetManager.registerPacket(23, Packet23DeviceList.class); this.packetManager.registerPacket(24, Packet24DeviceResolve.class); @@ -165,6 +182,7 @@ public class Boot { this.packetManager.registerExecutor(10, new Executor10RequestUpdate(this.serverConfiguration)); this.packetManager.registerExecutor(11, new Executor11Typeing(this.clientManager, this.packetManager)); this.packetManager.registerExecutor(15, new Executor15RequestTransport(this.serverConfiguration)); + this.packetManager.registerExecutor(16, new Executor16PushNotification()); this.packetManager.registerExecutor(24, new Executor24DeviceResolve(this.clientManager, this.eventManager)); } diff --git a/src/main/java/com/rosetta/im/Main.java b/src/main/java/com/rosetta/im/Main.java index 4b8defc..c9aedee 100644 --- a/src/main/java/com/rosetta/im/Main.java +++ b/src/main/java/com/rosetta/im/Main.java @@ -32,7 +32,7 @@ public class Main { } // Значение по умолчанию. - return 8080; + return 3000; } } \ No newline at end of file diff --git a/src/main/java/com/rosetta/im/database/Repository.java b/src/main/java/com/rosetta/im/database/Repository.java index 4aad181..d3f5177 100644 --- a/src/main/java/com/rosetta/im/database/Repository.java +++ b/src/main/java/com/rosetta/im/database/Repository.java @@ -236,6 +236,7 @@ public abstract class Repository { * @param noResultType если true, то не указывать тип результата в запросе, используется для запросов типа UPDATE и DELETE * @return список сущностей */ + @SuppressWarnings("deprecation") public QuerySession buildQuery(String queryString, HashMap parameters, boolean noResultType) { Session session = HibernateUtil.openSession(); try { diff --git a/src/main/java/com/rosetta/im/executors/Executor16PushNotification.java b/src/main/java/com/rosetta/im/executors/Executor16PushNotification.java new file mode 100644 index 0000000..2b506c9 --- /dev/null +++ b/src/main/java/com/rosetta/im/executors/Executor16PushNotification.java @@ -0,0 +1,47 @@ +package com.rosetta.im.executors; + +import com.rosetta.im.Failures; +import com.rosetta.im.client.tags.ECIAuthentificate; +import com.rosetta.im.database.entity.User; +import com.rosetta.im.database.repository.UserRepository; +import com.rosetta.im.packet.Packet16PushNotification; +import com.rosetta.im.service.services.UserService; + +import io.orprotocol.ProtocolException; +import io.orprotocol.client.Client; +import io.orprotocol.packet.PacketExecutor; + +public class Executor16PushNotification extends PacketExecutor { + + private final UserRepository userRepository = new UserRepository(); + private final UserService userService = new UserService(userRepository); + + @Override + public void onPacketReceived(Packet16PushNotification packet, Client client) throws Exception, ProtocolException { + ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class); + if(eciAuthentificate == null || !eciAuthentificate.hasAuthorized()){ + /** + * Клиент не авторизован, нельзя подписывать его на уведомления + */ + client.disconnect(Failures.HANDSHAKE_NOT_COMPLETED); + return; + } + String notificationToken = packet.getNotificationToken(); + if(notificationToken.isEmpty()){ + /** + * Клиент прислал пустой токен, отписывать его от уведомлений не нужно, а подписывать бессмысленно, просто игнорируем этот пакет + */ + return; + } + User user = userService.fromClient(client); + switch (packet.getAction()) { + case SUBSCRIBE: + userService.subscribeToPushNotifications(user, notificationToken); + break; + case UNSUBSCRIBE: + userService.unsubscribeFromPushNotifications(user, notificationToken); + break; + } + } + +} diff --git a/src/main/java/com/rosetta/im/packet/Packet16PushNotification.java b/src/main/java/com/rosetta/im/packet/Packet16PushNotification.java new file mode 100644 index 0000000..fb1844d --- /dev/null +++ b/src/main/java/com/rosetta/im/packet/Packet16PushNotification.java @@ -0,0 +1,63 @@ +package com.rosetta.im.packet; + +import com.rosetta.im.packet.runtime.NetworkNotificationAction; + +import io.orprotocol.Stream; +import io.orprotocol.packet.Packet; + +/** + * Packet16PushNotification бросается клиентом для подписки на пуш уведомления + */ +public class Packet16PushNotification extends Packet { + + private String notificationToken; + private NetworkNotificationAction action; + + @Override + public void read(Stream stream) { + this.notificationToken = stream.readString(); + this.action = NetworkNotificationAction.fromCode(stream.readInt8()); + } + + @Override + public Stream write() { + Stream stream = new Stream(); + stream.writeInt16(this.packetId); + stream.writeString(notificationToken); + stream.writeInt8(action.getCode()); + return stream; + } + + /** + * Получить токен пуш уведомлений, который нужно подписать или отписать в зависимости от action + * @return токен пуш уведомлений + */ + public String getNotificationToken() { + return notificationToken; + } + + /** + * Получить действие, которое нужно выполнить с токеном пуш уведомлений. SUBSCRIBE - подписать этот токен на пуш уведомления, UNSUBSCRIBE - отписать этот токен от пуш уведомлений + * @return действие, которое нужно выполнить с токеном пуш уведомлений + */ + public NetworkNotificationAction getAction() { + return action; + } + + /** + * Устанавливает токен пуш уведомлений, который нужно подписать или отписать в зависимости от action + * @param notificationToken токен пуш уведомлений + */ + public void setNotificationToken(String notificationToken) { + this.notificationToken = notificationToken; + } + + /** + * Устанавливает действие, которое нужно выполнить с токеном пуш уведомлений. SUBSCRIBE - подписать этот токен на пуш уведомления, UNSUBSCRIBE - отписать этот токен от пуш уведомлений + * @param action действие, которое нужно выполнить с токеном пуш уведомлений + */ + public void setAction(NetworkNotificationAction action) { + this.action = action; + } + +} diff --git a/src/main/java/com/rosetta/im/packet/runtime/NetworkNotificationAction.java b/src/main/java/com/rosetta/im/packet/runtime/NetworkNotificationAction.java new file mode 100644 index 0000000..9c64427 --- /dev/null +++ b/src/main/java/com/rosetta/im/packet/runtime/NetworkNotificationAction.java @@ -0,0 +1,34 @@ +package com.rosetta.im.packet.runtime; + +/** + * Используется в Packet16PushNotification для указания действия, которое нужно выполнить с токеном пуш уведомлений. + */ +public enum NetworkNotificationAction { + /** + * Подписать этот токен на пуш уведомления. Если токен уже был подписан, то ничего не произойдет. + */ + SUBSCRIBE(0), + /** + * Отписать этот токен от пуш уведомлений. Если токен не был подписан, то ничего не произойдет. + */ + UNSUBSCRIBE(1); + + private final int code; + + NetworkNotificationAction(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public static NetworkNotificationAction fromCode(int code) { + for (NetworkNotificationAction action : values()) { + if (action.code == code) { + return action; + } + } + throw new IllegalArgumentException("Unknown NetworkNotificationAction code: " + code); + } +} diff --git a/src/main/java/com/rosetta/im/service/services/UserService.java b/src/main/java/com/rosetta/im/service/services/UserService.java index 451a722..6725de1 100644 --- a/src/main/java/com/rosetta/im/service/services/UserService.java +++ b/src/main/java/com/rosetta/im/service/services/UserService.java @@ -60,4 +60,34 @@ public class UserService extends Service { return user != null; } + /** + * Подписывает пользователя на пуш уведомления, добавляя токен в его список токенов. Если токен уже был добавлен, то ничего не произойдет. + * @param user пользователь, которого нужно подписать на пуш уведомления + * @param notificationToken токен пуш уведомлений, который нужно добавить пользователю. Если токен уже был добавлен, то ничего не произойдет + */ + public void subscribeToPushNotifications(User user, String notificationToken) { + List tokens = user.getNotificationsTokens(); + if(tokens.contains(notificationToken)){ + return; + } + tokens.add(notificationToken); + user.setNotificationsTokens(tokens); + this.getRepository().update(user); + } + + /** + * Отписывает пользователя от пуш уведомлений, удаляя токен из его списка токенов. Если токена не было, то ничего не произойдет. + * @param user пользователь, которого нужно отписать от пуш уведомлений + * @param notificationToken токен пуш уведомлений, который нужно удалить у пользователя. Если токена не было, то ничего не произойдет + */ + public void unsubscribeFromPushNotifications(User user, String notificationToken) { + List tokens = user.getNotificationsTokens(); + if(!tokens.contains(notificationToken)){ + return; + } + tokens.remove(notificationToken); + user.setNotificationsTokens(tokens); + this.getRepository().update(user); + } + } diff --git a/src/main/java/io/orprotocol/Server.java b/src/main/java/io/orprotocol/Server.java index 9adfe95..0f4350c 100644 --- a/src/main/java/io/orprotocol/Server.java +++ b/src/main/java/io/orprotocol/Server.java @@ -95,6 +95,7 @@ public class Server extends WebSocketServer { } } + @SuppressWarnings("unchecked") @Override public void onMessage(WebSocket socket, ByteBuffer byteBuffer) { Client client = socket.getAttachment(); diff --git a/src/main/java/io/orprotocol/client/Client.java b/src/main/java/io/orprotocol/client/Client.java index 4aacab3..e42cb45 100644 --- a/src/main/java/io/orprotocol/client/Client.java +++ b/src/main/java/io/orprotocol/client/Client.java @@ -149,6 +149,7 @@ public class Client { * @param key Ключ данных. * @return Значение данных. */ + @SuppressWarnings("unchecked") public T getTag(Class eciTagClass) { return (T) this.eciTags.get(eciTagClass); }