Compare commits

..

8 Commits

Author SHA1 Message Date
d4125846ec Merge pull request 'Видеосообщения (кружочки)' (#19) from dev into main
All checks were successful
Build rosetta-wss / build (push) Successful in 1m57s
Reviewed-on: #19
2026-04-11 15:56:05 +00:00
RoyceDa
f87198c054 Видеосообщения (кружочки) 2026-04-10 17:36:31 +02:00
RoyceDa
58fe3c409d Новый тип вложений - голосовое сообщение
All checks were successful
Build rosetta-wss / build (push) Successful in 1m51s
2026-04-10 17:28:32 +02:00
RoyceDa
bdc44f36f0 Push READ теперь с пустым уведомлением для точного пробуждения
All checks were successful
Build rosetta-wss / build (push) Successful in 1m57s
2026-04-08 22:11:52 +02:00
RoyceDa
145aaf8288 Исправление кика с сервера при гонке с закрытием RTC на SFU
All checks were successful
Build rosetta-wss / build (push) Successful in 1m31s
2026-04-07 15:24:45 +02:00
RoyceDa
ddcd08aeae END_CALL для остальных устройств принявшего звонок пользователя 2026-04-07 15:16:00 +02:00
RoyceDa
435d6fefa8 Защита от подмены src в пакетах 2026-04-07 15:10:27 +02:00
RoyceDa
986cd765d8 Дополнительные комментарии 2026-04-07 14:56:56 +02:00
5 changed files with 89 additions and 19 deletions

View File

@@ -18,7 +18,9 @@ public class CallSession {
* Клиенты в этом списке не могут принимать другие звонки, так как они уже заняты дозвоном, * Клиенты в этом списке не могут принимать другие звонки, так как они уже заняты дозвоном,
* но они еще не в звонке, так как не приняли звонок * но они еще не в звонке, так как не приняли звонок
* *
* Клиенты удаляются из этого списка, когда они принимают звонок или отклоняют его, тогда они либо переходят в звонок, либо становятся свободными для других звонков * Клиенты удаляются из этого списка, когда они принимают звонок или отклоняют его,
* тогда они либо переходят в звонок, либо становятся свободными для других звонков
* pk -> время начала дозвона (timestamp в миллисекундах)
*/ */
public HashMap<String, Long> ringing; public HashMap<String, Long> ringing;
/** /**
@@ -48,6 +50,16 @@ public class CallSession {
this.clients.put(publicKey, client); this.clients.put(publicKey, client);
} }
/**
* Проверяет, может ли этот публичный ключ выполнять какие-либо действия в рамках этой сессии звонка,
* чтобы не допустить выполнение действий от посторонних публичных ключей, которые не участвуют в звонке
* @param publicKey Публичный ключ для проверки
* @return true, если этот публичный ключ может выполнять действия в рамках этой сессии звонка, false иначе
*/
public boolean isValidSource(String publicKey) {
return this.ringing.containsKey(publicKey) || this.clients.containsKey(publicKey);
}
/** /**
* Получаем публичный ключ клиента по его сокету, чтобы понимать, кто отправляет сигналы в рамках звонка * Получаем публичный ключ клиента по его сокету, чтобы понимать, кто отправляет сигналы в рамках звонка
* @param client Сокет клиента, для которого нужно получить публичный ключ * @param client Сокет клиента, для которого нужно получить публичный ключ

View File

@@ -56,9 +56,10 @@ public class ClientManager {
* Отправить пакет ВСЕМ АВТОРИЗОВАННЫМ клиентам с публичным ключом publicKey * Отправить пакет ВСЕМ АВТОРИЗОВАННЫМ клиентам с публичным ключом publicKey
* @param publicKey публичный ключ получателя * @param publicKey публичный ключ получателя
* @param packet пакет для отправки * @param packet пакет для отправки
* @param exclude клиент, который не должен получать этот пакет, может быть null
* @throws ProtocolException если произошла ошибка при отправке пакета клиенту * @throws ProtocolException если произошла ошибка при отправке пакета клиенту
*/ */
public void sendPacketToAuthorizedPK(String publicKey, Packet packet) throws ProtocolException { public void sendPacketToAuthorizedPK(String publicKey, Packet packet, Client exclude) throws ProtocolException {
Set<Client> clients = this.clientIndexer.getClients(ECIAuthentificate.class, "publicKey", publicKey); Set<Client> clients = this.clientIndexer.getClients(ECIAuthentificate.class, "publicKey", publicKey);
if(clients == null){ if(clients == null){
/** /**
@@ -77,12 +78,55 @@ public class ClientManager {
continue; continue;
} }
/** /**
* Отправляем пакет каждому клиенту с таким публичным ключом (то есть всем его авторизованным сессиям/устройствам) * Отправляем пакет каждому клиенту с таким публичным ключом (то есть всем его авторизованным сессиям/устройствам),
* исключая клиента exclude
*/ */
if(exclude != null && client.equals(exclude)){
/**
* Этот клиент является исключением, он не должен получать этот пакет
*/
continue;
}
client.send(packet); client.send(packet);
} }
} }
/**
* Отправить пакет ВСЕМ АВТОРИЗОВАННЫМ клиентам с публичным ключом publicKey
* @param publicKey публичный ключ получателя
* @param packet пакет для отправки
* @throws ProtocolException если произошла ошибка при отправке пакета клиенту
*/
public void sendPacketToAuthorizedPK(String publicKey, Packet packet) throws ProtocolException {
this.sendPacketToAuthorizedPK(publicKey, packet, null);
}
/**
* Отправить пакет всем клиентам с публичными ключами из списка publicKeys
* @param publicKeys список публичных ключей получателей
* @param packet пакет для отправки
* @param exclude клиент, который не должен получать этот пакет, может быть null
* @throws ProtocolException если произошла ошибка при отправке пакета клиенту
*/
public void sendPacketToAuthorizedPK(List<String> publicKeys, Packet packet, Client exclude) throws ProtocolException {
for(String publicKey : publicKeys){
this.sendPacketToAuthorizedPK(publicKey, packet, exclude);
}
}
/**
* Отправить пакет всем клиентам с публичными ключами из списка publicKeys
* @param publicKeys список публичных ключей получателей
* @param packet пакет для отправки
* @throws ProtocolException если произошла ошибка при отправке пакета клиенту
*/
public void sendPacketToAuthorizedPK(List<String> publicKeys, Packet packet) throws ProtocolException {
for(String publicKey : publicKeys){
this.sendPacketToAuthorizedPK(publicKey, packet, null);
}
}
/** /**
* Отправить пакет всем клиентам с публичными ключом как у client, кроме клиента client, который является отправителем и не должен получать этот пакет * Отправить пакет всем клиентам с публичными ключом как у client, кроме клиента client, который является отправителем и не должен получать этот пакет
* @param client клиент * @param client клиент
@@ -111,18 +155,6 @@ public class ClientManager {
} }
} }
/**
* Отправить пакет всем клиентам с публичными ключами из списка publicKeys
* @param publicKeys список публичных ключей получателей
* @param packet пакет для отправки
* @throws ProtocolException если произошла ошибка при отправке пакета клиенту
*/
public void sendPacketToAuthorizedPK(List<String> publicKeys, Packet packet) throws ProtocolException {
for(String publicKey : publicKeys){
this.sendPacketToAuthorizedPK(publicKey, packet);
}
}
/** /**
* Получить список клиентов по публичному ключу (get PublicKey clients), могут быть неавторизованные клиенты * Получить список клиентов по публичному ключу (get PublicKey clients), могут быть неавторизованные клиенты
* @param publicKey публичный ключ клиента * @param publicKey публичный ключ клиента

View File

@@ -127,6 +127,13 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
client.disconnect(Failures.NO_CALL_SESSION); client.disconnect(Failures.NO_CALL_SESSION);
return; return;
} }
if(!session.isValidSource(src)) {
/**
* Клиент не состоит в сессии звонка, отключаем его от сервера, так как он отправляет некорректные данные
*/
client.disconnect(Failures.DATA_MISSMATCH);
return;
}
Room room = this.fus.createRoom(); Room room = this.fus.createRoom();
session.setRoom(room); session.setRoom(room);
session.joinCall(src, client); session.joinCall(src, client);
@@ -134,6 +141,13 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
room.addParticipant(participant); room.addParticipant(participant);
} }
session.sendPacket(packet, client); session.sendPacket(packet, client);
/**
* Сбрасываем вызов на всех остальных устройствах пользователя, который принимает звонок,
* чтобы он не смог принять или отклонить звонок с другого устройства
*/
Packet26SignalPeer endCallOtherDevices = new Packet26SignalPeer();
endCallOtherDevices.setSignalType(NetworkSignalType.END_CALL);
this.clientManager.sendPacketToAuthorizedPK(src, endCallOtherDevices, client);
return; return;
} }
if(type == NetworkSignalType.KEY_EXCHANGE){ if(type == NetworkSignalType.KEY_EXCHANGE){
@@ -170,13 +184,22 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
} }
if(session == null) { if(session == null) {
/** /**
* Сессии звонка нет * Сессии звонка нет, скорее всего она была удалена при обрыве RTC Peer Connection,
* при срабатывании RTCPeerConnection::close на клиенте раньше, чем клиент отправил сигнал END_CALL
*/ */
client.disconnect(Failures.NO_CALL_SESSION);
return; return;
} }
/**
* Отправляем сигнал окончания звонка всем участникам сессии, кроме отправителя
*/
session.sendPacket(packet, client); session.sendPacket(packet, client);
/**
* Отправляем пакет вызываемым (ringing) пользователям (которые еще не в сессии)
*/
this.callManager.sendPacketToRinging(session, packet); this.callManager.sendPacketToRinging(session, packet);
/**
* Удаляем сессию из активных сессий звонков
*/
this.callManager.removeSession(session); this.callManager.removeSession(session);
return; return;
} }

View File

@@ -8,7 +8,9 @@ public enum AttachmentType {
MESSAGES(1), MESSAGES(1),
FILE(2), FILE(2),
AVATAR(3), AVATAR(3),
CALL(4); CALL(4),
VOICE(5),
VIDEO_CIRCLE(6);
private final int code; private final int code;

View File

@@ -61,9 +61,10 @@ public class FCM extends Pusher {
* Тихий тип уведомления для очистки отправленных уведомлений на устройстве, * Тихий тип уведомления для очистки отправленных уведомлений на устройстве,
* не должен отображаться пользователю, поэтому не задаем звук и ставим contentAvailable для iOS и high priority для Android * не должен отображаться пользователю, поэтому не задаем звук и ставим contentAvailable для iOS и high priority для Android
*/ */
apnsConfig.setAps(Aps.builder().setContentAvailable(true).setSound("default").build()); apnsConfig.setAps(Aps.builder().setContentAvailable(true).setMutableContent(true).build());
androidConfig.setPriority(AndroidConfig.Priority.HIGH); androidConfig.setPriority(AndroidConfig.Priority.HIGH);
messageBuilder.setApnsConfig(apnsConfig.build()); messageBuilder.setApnsConfig(apnsConfig.build());
messageBuilder.setNotification(Notification.builder().setTitle("").setBody("").build());
messageBuilder.setAndroidConfig(androidConfig.build()); messageBuilder.setAndroidConfig(androidConfig.build());
break; break;
case PushType.PERSONAL_MESSAGE: case PushType.PERSONAL_MESSAGE: