Новый протокол регистрации токенов
All checks were successful
Build rosetta-wss / build (push) Successful in 1m48s

This commit is contained in:
RoyceDa
2026-03-31 17:44:09 +02:00
parent 1e00105d87
commit d2263c6b9a
14 changed files with 391 additions and 169 deletions

View File

@@ -12,6 +12,7 @@ import im.rosetta.database.repository.UserRepository;
import im.rosetta.packet.Packet11Typeing;
import im.rosetta.packet.Packet7Read;
import im.rosetta.packet.base.PacketBaseDialog;
import im.rosetta.service.dispatch.push.PushNotifyDispatcher;
import im.rosetta.service.dispatch.runtime.PushType;
import im.rosetta.service.services.BufferService;
import im.rosetta.service.services.UserService;
@@ -32,7 +33,7 @@ public class MessageDispatcher {
private final ClientManager clientManager;
private final BufferRepository bufferRepository = new BufferRepository();
private final BufferService bufferService;
private final FirebaseDispatcher firebaseDispatcher = new FirebaseDispatcher();
private final PushNotifyDispatcher pushNotifyDispatcher = new PushNotifyDispatcher();
private final UserRepository userRepository = new UserRepository();
private final UserService userService = new UserService(userRepository);
@@ -103,7 +104,7 @@ public class MessageDispatcher {
* Если это пакет прочтения, то отправляем тихий пуш, что диалог прочитан, отправляем тому, кто читает диалог, чтобы
* клиент мог очистить пуши для этого диалога
*/
this.firebaseDispatcher.sendPushNotification(fromPublicKey, new HashMap<>(){
this.pushNotifyDispatcher.sendPush(fromPublicKey, new HashMap<>(){
{
put("type", PushType.READ);
put("dialog", toPublicKey);
@@ -114,7 +115,7 @@ public class MessageDispatcher {
/**
* Отправляем PUSH уведомление
*/
this.firebaseDispatcher.sendPushNotification(groupMembersPublicKeys, new HashMap<>(){
this.pushNotifyDispatcher.sendPush(groupMembersPublicKeys, new HashMap<>(){
{
put("type", PushType.GROUP_MESSAGE);
put("dialog", toPublicKey.replace("#group:", ""));
@@ -173,7 +174,7 @@ public class MessageDispatcher {
* Если это пакет прочтения, то отправляем тихий пуш, что диалог прочитан, отправляем тому, кто читает диалог, чтобы
* клиент мог очистить пуши для этого диалога
*/
this.firebaseDispatcher.sendPushNotification(fromPublicKey, new HashMap<>(){
this.pushNotifyDispatcher.sendPush(fromPublicKey, new HashMap<>(){
{
put("type", PushType.READ);
put("dialog", toPublicKey);
@@ -185,7 +186,7 @@ public class MessageDispatcher {
/**
* Отправляем PUSH уведомление получателю
*/
this.firebaseDispatcher.sendPushNotification(toPublicKey, new HashMap<>(){
this.pushNotifyDispatcher.sendPush(toPublicKey, new HashMap<>(){
{
put("type", PushType.PERSONAL_MESSAGE);
put("dialog", fromPublicKey);

View File

@@ -0,0 +1,78 @@
package im.rosetta.service.dispatch.push;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import im.rosetta.database.entity.Device;
import im.rosetta.database.entity.PushToken;
import im.rosetta.database.repository.DeviceRepository;
import im.rosetta.packet.runtime.TokenType;
import im.rosetta.service.dispatch.push.dispatchers.FCM;
import im.rosetta.service.dispatch.push.dispatchers.VoIPApns;
import im.rosetta.service.services.DeviceService;
public class PushNotifyDispatcher {
private DeviceRepository deviceRepository = new DeviceRepository();
private DeviceService deviceService = new DeviceService(deviceRepository);
private final ExecutorService executor = Executors.newFixedThreadPool(10);
private final HashMap<TokenType, Pusher> pushers = new HashMap<>() {{
put(TokenType.FCM, new FCM());
put(TokenType.VoIPApns, new VoIPApns());
}};
private Pusher findPusher(TokenType tokenType){
return this.pushers.get(tokenType);
}
private List<PushToken> findPushTokens(String publicKey) {
List<Device> devices = this.deviceService.getDevicesByPK(publicKey);
List<PushToken> pushTokens = new java.util.ArrayList<>();
for(Device device : devices){
pushTokens.addAll(device.getTokens());
}
return pushTokens;
}
/**
* Отправить уведомление пользователю с publicKey, используя все его токены для отправки уведомления, если таковые имеются
* @param publicKey публичный ключ пользователя, которому нужно отправить уведомление
* @param data данные уведомления
*/
public void sendPush(String publicKey, HashMap<String, String> data) {
executor.execute(() -> {
List<PushToken> pushTokens = this.findPushTokens(publicKey);
for(PushToken pushToken : pushTokens){
Pusher pusher = this.findPusher(pushToken.getType());
if(pusher != null){
pusher.sendPush(pushToken.getToken(), data);
}
}
});
}
/**
* Отправить уведомление пользователям с publicKeys, используя все их токены для отправки уведомления, если таковые имеются
* @param publicKeys список публичных ключей пользователей, которым нужно отправить уведомление
* @param data данные уведомления
*/
public void sendPush(List<String> publicKeys, HashMap<String, String> data) {
executor.execute(() -> {
for(String publicKey : publicKeys){
List<PushToken> pushTokens = this.findPushTokens(publicKey);
for(PushToken pushToken : pushTokens){
Pusher pusher = this.findPusher(pushToken.getType());
if(pusher != null){
pusher.sendPush(pushToken.getToken(), data);
}
}
}
});
}
}

View File

@@ -0,0 +1,14 @@
package im.rosetta.service.dispatch.push;
import java.util.HashMap;
public abstract class Pusher {
/**
* Вызывается при отправке PUSH уведомления, когда уже определен токен
* @param token токен для уведомления (FCM/VoIP/etc..)
* @param data данные уведомления
*/
public abstract void sendPush(String token, HashMap<String, String> data);
}

View File

@@ -1,11 +1,8 @@
package im.rosetta.service.dispatch;
package im.rosetta.service.dispatch.push.dispatchers;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
@@ -17,20 +14,12 @@ import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import im.rosetta.database.repository.UserRepository;
import im.rosetta.service.dispatch.push.Pusher;
import im.rosetta.service.dispatch.runtime.PushType;
import im.rosetta.service.services.UserService;
/**
* Класс для отправки push-уведомлений пользователям через Firebase Cloud Messaging (FCM).
*/
public class FirebaseDispatcher {
public class FCM extends Pusher {
private UserRepository userRepository = new UserRepository();
private UserService userService = new UserService(userRepository);
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public FirebaseDispatcher() {
public FCM() {
initializeFirebase();
}
@@ -103,66 +92,14 @@ public class FirebaseDispatcher {
return messageBuilder.build();
}
public void sendPushNotification(String publicKey, HashMap<String, String> data) {
executor.submit(() -> {
try {
List<String> tokens = userService.getNotificationsTokens(publicKey);
if (tokens == null || tokens.isEmpty()) {
return;
}
for (String token : tokens) {
try {
Message message = this.buildMessage(token, data);
FirebaseMessaging.getInstance().send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* Отправляет push-уведомление нескольким пользователям (асинхронно)
* @param publicKeys список публичных ключей пользователей
* @param data данные уведомления
*/
public void sendPushNotification(List<String> publicKeys, HashMap<String, String> data) {
executor.submit(() -> {
for (String publicKey : publicKeys) {
sendPushNotificationSync(publicKey, data);
}
});
}
private void sendPushNotificationSync(String publicKey, HashMap<String, String> data) {
try {
List<String> tokens = userService.getNotificationsTokens(publicKey);
if (tokens == null || tokens.isEmpty()) {
return;
}
for (String token : tokens) {
try {
Message message = this.buildMessage(token, data);
FirebaseMessaging.getInstance().send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
// Логирование ошибки
@Override
public void sendPush(String token, HashMap<String, String> data) {
try{
Message message = this.buildMessage(token, data);
FirebaseMessaging.getInstance().send(message);
}catch(Exception e){
e.printStackTrace();
}
}
/**
* Завершить работу executor при остановке приложения
*/
public void shutdown() {
executor.shutdown();
}
}
}

View File

@@ -0,0 +1,15 @@
package im.rosetta.service.dispatch.push.dispatchers;
import java.util.HashMap;
import im.rosetta.service.dispatch.push.Pusher;
public class VoIPApns extends Pusher {
@Override
public void sendPush(String token, HashMap<String, String> data) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'sendPush'");
}
}

View File

@@ -59,57 +59,4 @@ public class UserService extends Service<UserRepository> {
User user = this.getRepository().findByField("username", username);
return user != null;
}
/**
* Подписывает пользователя на пуш уведомления, добавляя токен в его список токенов. Если токен уже был добавлен, то ничего не произойдет.
* @param user пользователь, которого нужно подписать на пуш уведомления
* @param notificationToken токен пуш уведомлений, который нужно добавить пользователю. Если токен уже был добавлен, то ничего не произойдет
*/
public void subscribeToPushNotifications(User user, String notificationToken) {
List<String> 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<String> tokens = user.getNotificationsTokens();
if(!tokens.contains(notificationToken)){
return;
}
tokens.remove(notificationToken);
user.setNotificationsTokens(tokens);
this.getRepository().update(user);
}
/**
* Получает список токенов пуш уведомлений пользователя
* @param user пользователь, у которого нужно получить список токенов пуш уведомлений
* @return список токенов пуш уведомлений пользователя
*/
public List<String> getNotificationsTokens(User user) {
return user.getNotificationsTokens();
}
/**
* Получает список токенов пуш уведомлений пользователя
* @param publicKey публичный ключ пользователя, у которого нужно получить список токенов пуш уведомлений
* @return список токенов пуш уведомлений пользователя
*/
public List<String> getNotificationsTokens(String publicKey) {
User user = this.getRepository().findByField("publicKey", publicKey);
if(user == null){
return null;
}
return user.getNotificationsTokens();
}
}