package im.rosetta.service.dispatch; import java.util.HashSet; import java.util.List; import im.rosetta.client.ClientManager; import im.rosetta.client.tags.ECIAuthentificate; import im.rosetta.database.repository.BufferRepository; import im.rosetta.database.repository.GroupRepository; import im.rosetta.packet.base.PacketBaseDialog; import im.rosetta.service.services.BufferService; 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; 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 toPublicKey = packet.getToPublicKey(); List groupMembersPublicKeys = this.groupRepository.findGroupMembers(toPublicKey.replace("#group:", "")); if(groupMembersPublicKeys.isEmpty()){ /** * Если группа не найдена или в группе нет участников, то в такую отправить * сообщение нельзя, ничего не делаем */ return; } if(!groupMembersPublicKeys.contains(eciAuthentificate.getPublicKey())){ /** * Если отправитель не является участником группы, то он не может отправлять * сообщения в эту группу */ return; } /** * Отправляем всем участникам группы, кроме отправителя этот пакет, попутно не забывая проверить, а не один ли он в группе */ groupMembersPublicKeys.remove(eciAuthentificate.getPublicKey()); if(groupMembersPublicKeys.isEmpty()){ /** * Если отправитель был единственным участником группы, то отправлять сообщение некуда, * не кикаем пользователя */ return; } this.clientManager.sendPacketToAuthorizedPK(groupMembersPublicKeys, packet); //TODO: Сохранить сообщение в буфер для группы, чтобы группы тоже синхронизировались } /** * Отправляет личное сообщение получателю * @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(); this.clientManager.sendPacketToAuthorizedPK(toPublicKey, packet); if(!bufferizationNeed){ /** * Указан флаг, что буферизация не нужна, сообщения с этим флагом не будут доставлены если * оппонент оффлайн */ return; } /** * Сохраняем сообщение в буфер на случай если получатель офлайн, или нам нужна будет синхронизация сообщений для получателя */ this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey, packet); /** * Ретранслируем сообщение всем авторизованным сессиям отправителя, чтобы синхронизировать отправленные сообщения */ this.retranslate(packet, client); } /** * Отправляет личное сообщение получателю с буферизацией * @param packet пакет сообщения * @param client клиент отправляющий пакет * @throws ProtocolException */ public void sendPeer(PacketBaseDialog packet, Client client) throws ProtocolException { /** * По умолчанию буферизация включена, чтобы не терять сообщения */ this.sendPeer(packet, client, true); } /** * Сообщает всем авторизованным сессиям отправителя о том, что он отправил сообщения, * для того чтобы синхронизировать отправленные сообщения на всех устройствах отправителя * @param packet пакет сообщения * @param client клиент отправляющий пакет * @throws ProtocolException */ public void retranslate(PacketBaseDialog packet, Client client) throws ProtocolException { ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class); HashSet clients = this.clientManager.getClientIndexer() .getClients(ECIAuthentificate.class, "publicKey", eciAuthentificate.getPublicKey()); if(clients == null){ /** * Нет авторизованных сессий с таким публичным ключом */ return; } for(Client c : clients){ /** * Проходим по всем устройствам с таким публичным ключом и ретранслируем им пакет, кроме того устройства что * отправило пакет */ if(c.equals(client)){ continue; } c.send(packet); } } }