All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.intelie.live.plugins.messenger.api.MessengerServiceImpl Maven / Gradle / Ivy

The newest version!
package net.intelie.live.plugins.messenger.api;

import com.google.common.collect.ImmutableMap;
import net.intelie.live.*;
import net.intelie.live.plugins.feed.api.FeedDef;
import net.intelie.live.plugins.feed.api.FeedHelpers;
import net.intelie.live.plugins.feed.api.FeedService;
import net.intelie.live.plugins.messenger.chat.*;
import net.intelie.live.plugins.messenger.connections.MessengerConnectionListener;
import net.intelie.live.plugins.messenger.connections.MessengerConnectionsRegistry;
import net.intelie.live.plugins.messenger.connections.UserConnection;
import net.intelie.live.plugins.messenger.data.*;
import net.intelie.pipes.util.Escapes;
import org.jetbrains.annotations.NotNull;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.*;

import static net.intelie.live.Permission.ADMIN;
import static net.intelie.live.plugins.messenger.MessengerPermission.CREATE_ROOMS;
import static net.intelie.live.plugins.messenger.MessengerPermission.USE_MESSENGER;
import static net.intelie.live.plugins.messenger.chat.MessengerConstants.*;

public class MessengerServiceImpl implements MessengerService {
    private final ChatManager chatManager;
    private final MessagesManager messagesManager;
    private final ControlActionsManager controlActionsManager;
    private final MessengerEventFilters eventFilters;
    private final MessengerConnectionsRegistry connectionsRegistry;
    private final UserRoomSettingsManager userRoomSettingsManager;
    private final UserDataResolver userDataResolver;
    private final Live live;
    private final FeedService feedService;
    private final LoggedUser loggedUser;

    public MessengerServiceImpl(Live live, ChatManager chatManager, MessagesManager messagesManager, ControlActionsManager controlActionsManager,
                                MessengerEventFilters eventFilters, MessengerConnectionsRegistry connectionsRegistry, UserRoomSettingsManager userRoomSettingsManager,
                                UserDataResolver userDataResolver) throws Exception {
        this.live = live;
        this.chatManager = chatManager;
        this.messagesManager = messagesManager;
        this.controlActionsManager = controlActionsManager;
        this.eventFilters = eventFilters;
        this.connectionsRegistry = connectionsRegistry;
        this.userRoomSettingsManager = userRoomSettingsManager;
        this.userDataResolver = userDataResolver;
        this.feedService = live.system().getPluginService(FeedService.class);
        this.loggedUser = live.auth().getLoggedUser();
        this.initOrUpdateGlobalRoom();
    }

    public UserMessage sendMessage(String roomId, String message) {
        return this.messagesManager.send(roomId, message);
    }

    public UserMessage updateMessage(UserMessage message) throws Exception {
        UserDef user = loggedUser.getUser();
        return this.messagesManager.update(message, false, user.getId());
    }

    public UserMessage deleteMessage(String roomId, String messageId) throws Exception {
        UserDef user = loggedUser.getUser();
        return this.messagesManager.deleteById(messageId, isRoomAdmin(roomId), user.getId());
    }

    public RoomData createRoom(String roomName) throws Exception {
        if (roomNameAlreadyExists(roomName))
            throw badRequestException(ROOM_NAME_ALREADY_EXISTS);

        UserDef userDef = this.loggedUser.getUser();
        RoomData createdRoom = this.chatManager.createRoom(roomName, MessengerRoomTypes.ACTIVE_ROOM, new UserData(userDef.getId(), userDef.getUsername()));
        controlActionsManager.roomCreatedAction(createdRoom);
        return createdRoom;
    }

    public void deleteRoom(String roomId) throws Exception {
        RoomState roomState = chatManager.getRoom(roomId);
        if (MessengerRoomTypes.GLOBAL_ROOM.equals(roomState.getType()))
            throw badRequestException(CANNOT_DELETE_GLOBAL_ROOM);

        RoomData deletedRoom = this.chatManager.setRoomType(roomId, MessengerRoomTypes.DELETED_ROOM);
        controlActionsManager.roomDeletedAction(deletedRoom);
    }

    public RoomData renameRoom(String id, String newName) throws Exception {
        RoomData oldRoom = chatManager.getRoomData(id);

        if (oldRoom.getName().equals(newName))
            return oldRoom;

        if (roomNameAlreadyExists(newName))
            throw badRequestException(ROOM_NAME_ALREADY_EXISTS);
        if (MessengerRoomTypes.GLOBAL_ROOM.equals(oldRoom.getType()))
            throw badRequestException(CANNOT_UPDATE_GLOBAL_ROOM);

        RoomData updatedRoom = this.chatManager.renameRoom(id, newName);
        controlActionsManager.roomRenamedAction(oldRoom.getName(), updatedRoom);
        return updatedRoom;
    }

    public void archiveRoom(String roomId) throws Exception {
        RoomState roomState = chatManager.getRoom(roomId);
        if (MessengerRoomTypes.GLOBAL_ROOM.equals(roomState.getType()))
            throw badRequestException(CANNOT_UPDATE_GLOBAL_ROOM);

        RoomData archivedRoom = this.chatManager.setRoomType(roomId, MessengerRoomTypes.ARCHIVED_ROOM);
        controlActionsManager.roomArchivedAction(archivedRoom);
    }

    public void restoreRoom(String roomId) throws Exception {
        RoomState roomState = chatManager.getRoom(roomId);
        if (MessengerRoomTypes.GLOBAL_ROOM.equals(roomState.getType()))
            throw badRequestException(CANNOT_UPDATE_GLOBAL_ROOM);

        RoomData archivedRoom = this.chatManager.setRoomType(roomId, MessengerRoomTypes.ACTIVE_ROOM);
        controlActionsManager.roomRestoredAction(archivedRoom);
    }

    public void leaveRoom(String roomId) throws Exception {
        UserDef user = loggedUser.getUser();
        RoomState roomState = chatManager.getRoom(roomId);
        if (MessengerRoomTypes.GLOBAL_ROOM.equals(roomState.getType()))
            throw badRequestException(CANNOT_LEAVE_GLOBAL_ROOM);

        if (roomState.isUser(user.getId())) {
            if (roomState.isUniqueAdmin(user.getId())) {
                throw badRequestException(ROOM_HAS_NO_ADMIN);
            }
            this.removeUser(roomId, user.getId());
        }
    }

    public void setRoomIsMuted(String roomId, boolean opt) {
        UserDef user = loggedUser.getUser();
        RoomData roomData = chatManager.getRoomData(roomId);
        UserRoomState userRoomState = this.userRoomSettingsManager.getUserRoomState(user.getId(), roomId);

        if (userRoomState.isMuted() != opt) {
            UserRoomState updatedState = userRoomSettingsManager.setRoomMuted(user.getId(), roomId, opt);
            UserRoomData userRoomData = new UserRoomData(roomData, updatedState);
            controlActionsManager.roomMutedAction(userRoomData);
        }
    }

    public void setRoomIsFavorite(String roomId, boolean opt) {
        UserDef user = loggedUser.getUser();
        RoomData roomData = chatManager.getRoomData(roomId);
        UserRoomState userRoomState = this.userRoomSettingsManager.getUserRoomState(user.getId(), roomId);

        if (userRoomState.isFavorite() != opt) {
            UserRoomState updatedState = userRoomSettingsManager.setRoomFavorite(user.getId(), roomId, opt);
            UserRoomData userRoomData = new UserRoomData(roomData, updatedState);
            controlActionsManager.roomFavoriteAction(userRoomData);
        }
    }

    public synchronized void updateRoomUsers(String roomId, @NotNull List usersToAddOrUpdate, @NotNull List usersToRemove) throws Exception {
        if (usersToAddOrUpdate.isEmpty() && usersToRemove.isEmpty())
            throw badRequestException(USERS_LIST_EMPTY);

        RoomState roomState = this.chatManager.getRoom(roomId);
        if (roomState == null)
            throw badRequestException(ROOM_NOT_FOUND);

        if (MessengerRoomTypes.GLOBAL_ROOM.equals(roomState.getType())) {
            throw badRequestException(CANNOT_UPDATE_GLOBAL_ROOM);
        }

        List addedOrUpdatedUsers = new ArrayList<>();
        List removedUsers = new ArrayList<>();
        // adding or updating users
        for (UserData userData : usersToAddOrUpdate) {
            if (isInvalidLiveUser(userData.getId()))
                throw badRequestException(USER_NOT_FOUND_MSG);

            boolean isNewUser = false;
            if (!roomState.isUser(userData.getId())) {
                roomState.addUser(userData.getId());
                isNewUser = true;
            }
            roomState.makeAdmin(userData.getId(), userData.isAdmin());
            UserData updatedUser = userDataResolver.resolve(userData.getId());
            UserUpdateInfo userUpdateInfo = new UserUpdateInfo(updatedUser, isNewUser);
            userUpdateInfo.setAdmin(userData.isAdmin());
            addedOrUpdatedUsers.add(userUpdateInfo);
        }

        if (!roomState.hasAdmin())
            throw badRequestException(ROOM_HAS_NO_ADMIN);

        // removing users
        for (Integer userId : usersToRemove) {
            if (isInvalidLiveUser(userId))
                throw badRequestException(USER_NOT_FOUND_MSG);
            if (!roomState.isUser(userId))
                throw badRequestException(USER_NOT_MEMBER_OF_ROOM);
            if (roomState.isUniqueAdmin(userId))
                throw badRequestException(ROOM_HAS_NO_ADMIN);

            roomState.removeUser(userId);
            UserData removedUser = userDataResolver.resolve(userId);
            removedUsers.add(removedUser);
        }

        RoomData updatedRoom = chatManager.writeRoomState(roomId, roomState);
        controlActionsManager.updateRoomUsersAction(updatedRoom, addedOrUpdatedUsers, removedUsers);
    }

    public void addUser(String roomId, Integer userId) throws Exception {
        if (isInvalidLiveUser(userId))
            throw badRequestException(USER_NOT_FOUND_MSG);

        RoomData roomData = this.chatManager.addUser(roomId, userId);
        UserData addedUser = userDataResolver.resolve(userId);
        controlActionsManager.roomUserAddedAction(addedUser, roomData);
    }

    public void removeUser(String roomId, Integer userId) throws Exception {
        if (isInvalidLiveUser(userId))
            throw badRequestException(USER_NOT_FOUND_MSG);

        RoomState roomState = chatManager.getRoom(roomId);
        if (!roomState.isUser(userId))
            throw badRequestException(USER_NOT_MEMBER_OF_ROOM);
        if (roomState.isUniqueAdmin(userId))
            throw badRequestException(ROOM_HAS_NO_ADMIN);

        RoomData roomData = this.chatManager.removeUser(roomId, userId);
        UserData removedUser = userDataResolver.resolve(userId);
        controlActionsManager.roomUserRemovedAction(removedUser, roomData);
    }

    public void makeRoomAdmin(String roomId, Integer userId) throws Exception {
        if (isInvalidLiveUser(userId))
            throw badRequestException(USER_NOT_FOUND_MSG);

        RoomState roomState = chatManager.getRoom(roomId);
        if (!roomState.isUser(userId))
            throw badRequestException(USER_NOT_MEMBER_OF_ROOM);

        RoomData roomData = this.chatManager.makeAdmin(roomId, userId, true);
        UserData updatedUser = userDataResolver.resolve(userId);
        updatedUser.setAdmin(true);
        controlActionsManager.roomUserAdminChangedAction(updatedUser, roomData);
    }

    public void removeRoomAdmin(String roomId, Integer userId) throws Exception {
        if (isInvalidLiveUser(userId))
            throw badRequestException(USER_NOT_FOUND_MSG);

        RoomState roomState = chatManager.getRoom(roomId);
        if (!roomState.isAdmin(userId))
            throw badRequestException(USER_NOT_ROOM_ADMIN);
        if (roomState.isUniqueAdmin(userId))
            throw badRequestException(ROOM_HAS_NO_ADMIN);

        RoomData roomData = this.chatManager.makeAdmin(roomId, userId, false);
        UserData updatedUser = userDataResolver.resolve(userId);
        updatedUser.setAdmin(false);
        controlActionsManager.roomUserAdminChangedAction(updatedUser, roomData);
    }

    public List listRooms() {
        UserDef user = loggedUser.getUser();
        List rooms = new ArrayList<>();
        for (RoomData roomData : getAllowedRooms()) {
            UserRoomState state = userRoomSettingsManager.getUserRoomState(user.getId(), roomData.getId());
            rooms.add(new UserRoomData(roomData, state));
        }
        return rooms;
    }

    public List onlineUsers() {
        return this.connectionsRegistry.registeredUsers();
    }

    public boolean hasPermission(String id) {
        if (getAllowedRooms().stream().noneMatch(r -> r.getId().equals(id)))
            return false;

        RoomState roomState = chatManager.getRoom(id);
        return !MessengerRoomTypes.ARCHIVED_ROOM.equals(roomState.getType()) || isRoomAdmin(id);
    }

    public boolean isRoomAdmin(String roomId) {
        UserDef user = loggedUser.getUser();
        RoomState roomState = chatManager.getRoom(roomId);
        return user.isSuperuser() || (roomState.isUser(user.getId()) && roomState.isAdmin(user.getId()));
    }

    public List feed(String roomId, String remoteHost) throws Exception {
        return live.engine().registerBayeuxQueries(remoteHost, this.feedService.realtimeQuery(feedDef(roomId, false)));
    }

    public synchronized List feed(String remoteHost) throws Exception {
        UserDef userDef = loggedUser.getUser();
        QueryRequest queryRequest = this.feedService.realtimeQuery(feedDef());
        BayeuxQuery bayeuxQuery = new BayeuxQuery(remoteHost);
        bayeuxQuery.addQuery(queryRequest.makeAtom());
        UserConnection connection = new UserConnection(remoteHost, new UserData(userDef.getId(), userDef.getUsername()), userDef.isSuperuser());
        bayeuxQuery.listenerFactory(channel -> new MessengerConnectionListener(channel, connection, connectionsRegistry, eventFilters));
        return live.engine().registerBayeuxQuery(bayeuxQuery);
    }

    public List> scrollSync(String roomId, int count, Long cursor, String remoteHost) throws Exception {
        return FeedHelpers.sync(live.engine(), remoteHost, this.feedService.windowQuery(feedDef(roomId, false), count, cursor));
    }

    public List> countUnreadByRoom(int limit, String remoteHost) throws Exception {
        List> list = new ArrayList<>();
        for (UserRoomData room : listRooms()) {
            Integer count = countUnread(room.getId(), limit, remoteHost);
            Map map = new LinkedHashMap<>();
            map.put("id", room.getId());
            map.put("name", room.getName());
            map.put("unreadCount", count);
            list.add(map);
        }
        return list;
    }

    public Integer countUnread(int limit, String remoteHost) throws Exception {
        return FeedHelpers.count(live.engine(), remoteHost, this.listRooms().stream()
                .filter(roomData -> !roomData.isMuted() && !MessengerRoomTypes.ARCHIVED_ROOM.equals(roomData.getType()))
                .map(r -> feedService.windowQuery(feedDef(r.getId(), true), limit, getLastReadPlusOne(r.getId()), null))
                .toArray(QueryRequest[]::new));
    }

    public Integer countUnread(String id, int limit, String remoteHost) throws Exception {
        return FeedHelpers.count(live.engine(), remoteHost,
                feedService.windowQuery(feedDef(id, true), limit, getLastReadPlusOne(id), null));
    }

    public void setLastRead(String channel, UserRoomData.LastReadData lastRead) {
        UserDef user = loggedUser.getUser();
        userRoomSettingsManager.setLastRead(user.getId(), channel, lastRead);
    }

    private boolean isInvalidLiveUser(Integer userId) {
        return userDataResolver.resolve(userId) == null;
    }

    private boolean roomNameAlreadyExists(String name) {
        return chatManager.allRooms().stream()
                .filter(d -> !MessengerRoomTypes.DELETED_ROOM.equals(d.getType()))
                .anyMatch(d -> name.equals(d.getName()));
    }

    private FeedDef feedDef() {
        FeedDef feed = feedDefBasic();
        List feedItems = new ArrayList<>();
        feedItems.add(messagesManager.feedItem());
        feedItems.add(controlActionsManager.feedItem());
        feedItems.addAll(registeredFeedItems(null, null));
        feed.setItems(feedItems);
        return feed;
    }

    private FeedDef feedDef(String roomId, boolean isCountFeed) {
        RoomState room = chatManager.getRoom(roomId);
        FeedDef feed = feedDefBasic();
        List feedItems = new ArrayList<>();
        feedItems.add(messagesManager.feedItem(roomId));
        if (!isCountFeed) {
            feedItems.add(controlActionsManager.feedItem(roomId));
        }
        feedItems.addAll(registeredFeedItems(roomId, room));
        feed.setItems(feedItems);
        return feed;
    }

    private FeedDef feedDefBasic() {
        Integer userId = loggedUser.getUser().getId();
        FeedDef feed = new FeedDef();
        if (userId != null)
            feed.setPipes(Collections.singletonList("@filter feed_dashboard_visible_to_user(" + userId + ", dashboardId#)"));
        return feed;
    }

    private Set getAllowedRooms() {
        UserDef user = loggedUser.getUser();
        Set rooms = new LinkedHashSet<>();
        if (loggedUser.hasAnyOf(USE_MESSENGER, CREATE_ROOMS, ADMIN)) {
            rooms.addAll(chatManager.roomsVisibleToUser(user.getId(), user.isSuperuser()));
        }
        return rooms;
    }

    private List registeredFeedItems(String roomId, RoomState room) {
        List feedItems = new ArrayList<>();
        eventFilters.getEventFilters().forEach(eventFilter -> {
            FeedDef.FeedItem item = new FeedDef.FeedItem();
            item.setType(eventFilter.getType());
            item.setTimestamp(eventFilter.getTimestamp());

            final String filterRoomId = "room->id!:";
            if ((room != null) && MessengerRoomTypes.GLOBAL_ROOM.equals(room.getType()))
                item.setFilter(filterRoomId + "(-*" + (roomId != null ? "|" + Escapes.formatString(roomId) : "") + ")");
            else if (roomId != null)
                item.setFilter(filterRoomId + Escapes.formatString(roomId));

            feedItems.add(item);
        });

        return feedItems;
    }

    private Long getLastReadPlusOne(String channel) {
        UserDef user = loggedUser.getUser();
        UserRoomState state = userRoomSettingsManager.getUserRoomState(user.getId(), channel);
        if (state != null && state.lastReadState().getTimestamp() != null) {
            return state.lastReadState().getTimestamp() + 1;
        }
        return null;
    }

    private void initOrUpdateGlobalRoom() throws Exception {
        live.data().getContext().inTransaction(() -> {
            Set allRooms = chatManager.allRooms();
            if (allRooms.stream().anyMatch(r -> r.getType() == null)) {
                allRooms.forEach(r -> {
                    if (r.getType() == null) {
                        RoomState roomState = chatManager.getRoom(r.getId());
                        roomState.setType(MessengerRoomTypes.GLOBAL_ROOM);
                        roomState.setName(GLOBALROOM_NAME);
                        if (roomState.getCreatedAt() == null) {
                            roomState.setCreatedAt(System.currentTimeMillis());
                        }
                        chatManager.writeRoomState(r.getId(), roomState);
                    }
                });
            } else if (allRooms.stream().noneMatch(r -> MessengerRoomTypes.GLOBAL_ROOM.equals(r.getType()))) {
                chatManager.createRoom(GLOBALROOM_NAME, MessengerRoomTypes.GLOBAL_ROOM, null);
            }
            return null;
        });
    }

    private static Exception badRequestException(String reason) {
        return new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
                .entity(ImmutableMap.of("message", reason))
                .type(MediaType.APPLICATION_JSON_TYPE)
                .build());
    }

    @Override
    @NotNull
    public AutoCloseable registerEventTypeFilter(@NotNull String eventType) {
        MessengerEventFilters.EventFilter typeEventFilter = new MessengerEventFilters.EventFilter(eventType);
        typeEventFilter.setTimestamp("createdAt");
        eventFilters.registerEventFilter(typeEventFilter);
        return () -> eventFilters.removeEventFilter(typeEventFilter);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy