package im.rosetta.service.dispatch; import java.util.List; import im.rosetta.client.ClientManager; import im.rosetta.client.tags.ECIAuthentificate; import im.rosetta.database.entity.User; import im.rosetta.database.repository.BufferRepository; import im.rosetta.database.repository.GroupRepository; 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.services.BufferService; import im.rosetta.service.services.UserService; import io.orprotocol.ProtocolException; import io.orprotocol.client.Client; import io.orprotocol.packet.PacketManager; /** * Диспетчер сообщений, который отвечает за отправку сообщений получателям, а так же за сохранение сообщений в буфер для офлайн получателей и для синхронизации * Такой диспетчер нужен для того, чтобы не загромождать логику обработчиков сообщений, а так же для того, чтобы * централизовать логику отправки сообщений и сохранения их в буфер * Например, при отправке группового сообщения, диспетчер сам достает участников группы и * отправляет сообщение каждому участнику */ public class MessageDispatcher { private final GroupRepository groupRepository = new GroupRepository(); private final ClientManager clientManager; private final BufferRepository bufferRepository = new BufferRepository(); private final BufferService bufferService; private final FirebaseDispatcher firebaseDispatcher = new FirebaseDispatcher(); private final UserRepository userRepository = new UserRepository(); private final UserService userService = new UserService(userRepository); public MessageDispatcher(ClientManager clientManager, PacketManager packetManager) { this.clientManager = clientManager; this.bufferService = new BufferService(bufferRepository, packetManager); } /** * Отправляет групповое сообщение всем участникам группы, кроме отправителя * @param packet пакет с групповым сообщением */ public void sendGroup(PacketBaseDialog packet, Client client, ECIAuthentificate eciAuthentificate) throws ProtocolException { String fromPublicKey = packet.getFromPublicKey(); String toPublicKey = packet.getToPublicKey(); List groupMembersPublicKeys = this.groupRepository.findGroupMembers(toPublicKey.replace("#group:", "")); if(groupMembersPublicKeys.isEmpty()){ /** * Если группа не найдена или в группе нет участников, то в такую отправить * сообщение нельзя, ничего не делаем */ return; } if(!groupMembersPublicKeys.contains(eciAuthentificate.getPublicKey())){ /** * Если отправитель не является участником группы, то он не может отправлять * сообщения в эту группу */ return; } /** * Ретранслируем сообщение ВСЕМ авторизованным сессиям отправителя КРОМЕ текущей, * чтобы синхронизировать отправленные сообщения */ if(!(packet instanceof Packet11Typeing)){ /** * Если это пакет печати его не обязательно кэшировать, так как он нужен только * для отображения статуса печати в реальном времени * Кладем пакет в буфер для будущей синхронизации и на случай если кто-то из участников оффлайн, * в toPublicKey при отправке в группу у нас находится #group:groupId */ this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey.replace("#group:", ""), packet); this.clientManager.retranslate(client, packet); } /** * Отправляем всем участникам группы, кроме отправителя, попутно проверяем, а не один ли он в группе */ groupMembersPublicKeys.remove(eciAuthentificate.getPublicKey()); if(groupMembersPublicKeys.isEmpty()){ /** * Если отправитель был единственным участником группы, то отправлять сообщение некуда, * не кикаем пользователя */ return; } /** * Отправляем сообщение всем, кто в беседе */ this.clientManager.sendPacketToAuthorizedPK(groupMembersPublicKeys, packet); if(packet instanceof Packet11Typeing){ /** * Если это пакет печати, то не отправляем пуш уведомление, так как это может привести к спаму пушами при наборе текста */ return; } if(packet instanceof Packet7Read){ /** * Если это пакет прочтения, то не отправляем пуш уведомление, так как это может привести к спаму пушами при чтении сообщений */ return; } /** * Отправляем PUSH уведомление */ this.firebaseDispatcher.sendPushNotification(groupMembersPublicKeys, "Rosetta", "New message in group", toPublicKey.replace("#group:", "")); } /** * Отправляет личное сообщение получателю * @param packet пакет с личным сообщением * @param client клиент отправляющий пакет * @param bufferizationNeed флаг указывающий на то, что сообщение нужно буфферизировать, * чтобы доставить пользователям если они не онлайн, если указать false то этот пакет получит * только пользователь который были в сети */ public void sendPeer(PacketBaseDialog packet, Client client, boolean bufferizationNeed) throws ProtocolException { String fromPublicKey = packet.getFromPublicKey(); String toPublicKey = packet.getToPublicKey(); User user = this.userService.fromClient(client); if(user == null){ /** * Если пользователь не найден, то не отправляем сообщение, так как у нас нет информации о том, кто отправляет сообщение */ return; } /** * Ретранслируем сообщение ВСЕМ авторизованным сессиям отправителя КРОМЕ текущей, * чтобы синхронизировать отправленные сообщения */ this.clientManager.retranslate(client, packet); /** * Отправляем сообщение получателю */ this.clientManager.sendPacketToAuthorizedPK(toPublicKey, packet); if(!bufferizationNeed){ /** * Указан флаг, что буферизация не нужна, сообщения с этим флагом не будут доставлены если * оппонент оффлайн */ return; } /** * Сохраняем сообщение в буфер на случай если получатель офлайн, или нам нужна будет синхронизация сообщений для получателя */ this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey, packet); if(packet instanceof Packet11Typeing){ /** * Если это пакет печати, то не отправляем пуш уведомление, * так как это может привести к спаму пушами при наборе текста */ return; } if(packet instanceof Packet7Read){ /** * Если это пакет прочтения, то не отправляем пуш уведомление, * так как это может привести к спаму пушами при чтении сообщений */ return; } /** * Отправляем PUSH уведомление получателю */ this.firebaseDispatcher.sendPushNotification(toPublicKey, user.getTitle(), "New message", fromPublicKey); } /** * Отправляет личное сообщение получателю с буферизацией * @param packet пакет сообщения * @param client клиент отправляющий пакет * @throws ProtocolException */ public void sendPeer(PacketBaseDialog packet, Client client) throws ProtocolException { /** * По умолчанию буферизация включена, чтобы не терять сообщения */ this.sendPeer(packet, client, true); } }