/* * GT-Mconf: Multiconference system for interoperable web and mobile * http://www.inf.ufrgs.br/prav/gtmconf * PRAV Labs - UFRGS * * This file is part of Mconf-Mobile. * * Mconf-Mobile is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Mconf-Mobile is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Mconf-Mobile. If not, see <http://www.gnu.org/licenses/>. */ package org.mconf.bbb.chat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jboss.netty.channel.Channel; import org.mconf.bbb.BigBlueButtonClient.OnPrivateChatMessageListener; import org.mconf.bbb.BigBlueButtonClient.OnPublicChatMessageListener; import org.mconf.bbb.MainRtmpConnection; import org.mconf.bbb.Module; import org.mconf.bbb.api.ApplicationService; import org.mconf.bbb.users.IParticipant; import org.mconf.bbb.users.UsersModule; import org.red5.server.api.IAttributeStore; import org.red5.server.api.so.IClientSharedObject; import org.red5.server.api.so.ISharedObjectBase; import org.red5.server.api.so.ISharedObjectListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.flazr.rtmp.message.Command; import com.flazr.rtmp.message.CommandAmf0; public class ChatModule extends Module implements ISharedObjectListener { private static final Logger log = LoggerFactory.getLogger(ChatModule.class); private final IClientSharedObject publicChatSO, privateChatSO; private List<ChatMessage> publicChatMessages = Collections.synchronizedList(new ArrayList<ChatMessage>()); private Map<String, List<ChatMessage>> privateChatMessages = new ConcurrentHashMap<String, List<ChatMessage>>(); public final static int MESSAGE_ENCODING_UNKNOWN = -1; public final static int MESSAGE_ENCODING_STRING = 0; public final static int MESSAGE_ENCODING_TYPED_OBJECT = 1; public static int MESSAGE_ENCODING = MESSAGE_ENCODING_UNKNOWN; public ChatModule(MainRtmpConnection handler, Channel channel) { super(handler, channel); if (handler.getContext().getJoinService().getApplicationService().getVersion().equals(ApplicationService.VERSION_0_7)) MESSAGE_ENCODING = MESSAGE_ENCODING_STRING; else MESSAGE_ENCODING = MESSAGE_ENCODING_TYPED_OBJECT; publicChatSO = handler.getSharedObject("chatSO", false); publicChatSO.addSharedObjectListener(this); publicChatSO.connect(channel); privateChatSO = handler.getSharedObject(handler.getContext().getMyUserId(), false); privateChatSO.addSharedObjectListener(this); privateChatSO.connect(channel); } @Override public void onSharedObjectClear(ISharedObjectBase so) { log.debug("onSharedObjectClear"); if (so.equals(publicChatSO)) { publicChatMessages.clear(); doGetChatMessages(); return; } if (so.equals(privateChatSO)) { privateChatMessages.clear(); } } @Override public void onSharedObjectConnect(ISharedObjectBase so) { log.debug("onSharedObjectConnect"); } @Override public void onSharedObjectDelete(ISharedObjectBase so, String key) { log.debug("onSharedObjectDelete"); } @Override public void onSharedObjectDisconnect(ISharedObjectBase so) { log.debug("onSharedObjectDisconnect"); } @Override public void onSharedObjectSend(ISharedObjectBase so, String method, List<?> params) { log.debug("onSharedObjectSend"); if (so.equals(publicChatSO)) { if (method.equals("newChatMessage") && params != null) { // example: [oi|Felipe|0|14:35|en|97] onPublicChatMessage(new ChatMessage(params.get(0))); return; } } if (so.equals(privateChatSO)) { if (method.equals("messageReceived") && params != null) { // example: [97, oi|Felipe|0|14:35|en|97] String userId = UsersModule.getUserIdFromObject(params.get(0)); onPrivateChatMessage(new ChatMessage(params.get(1)), handler.getContext().getUsersModule().getParticipants().get(userId)); return; } } } @Override public void onSharedObjectUpdate(ISharedObjectBase so, String key, Object value) { log.debug("onSharedObjectUpdate 1"); } @Override public void onSharedObjectUpdate(ISharedObjectBase so, IAttributeStore values) { log.debug("onSharedObjectUpdate 2"); } @Override public void onSharedObjectUpdate(ISharedObjectBase so, Map<String, Object> values) { log.debug("onSharedObjectUpdate 3"); } /** * {@link} https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/src/org/bigbluebutton/modules/chat/services/PublicChatSharedObjectService.as#L128 */ public void doGetChatMessages() { Command cmd = new CommandAmf0("chat.getChatMessages", null); handler.writeCommandExpectingResult(channel, cmd); } /** * example: * [ARRAY [oi|Felipe|0|21:37|en|61, alo|Felipe|0|21:48|en|61, testando|Felipe|0|21:48|en|61]] * @param resultFor * @param command * @return */ /* */ public boolean onGetChatMessages(String resultFor, Command command) { if (resultFor.equals("chat.getChatMessages")) { publicChatMessages.clear(); List<Object> messages = (List<Object>) Arrays.asList((Object[]) command.getArg(0)); for (Object message : messages) publicChatMessages.add(new ChatMessage(message)); for (OnPublicChatMessageListener listener : handler.getContext().getPublicChatMessageListeners()) listener.onPublicChatMessage(publicChatMessages, handler.getContext().getUsersModule().getParticipants()); return true; } return false; } /** * {@link} https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/src/org/bigbluebutton/modules/chat/services/PublicChatSharedObjectService.as#L89 * @param message */ public void sendPublicChatMessage(String message) { ChatMessage chatMessage = new ChatMessage(); chatMessage.setMessage(message); chatMessage.setUsername(handler.getContext().getJoinService().getJoinedMeeting().getFullname()); chatMessage.setUserId(handler.getContext().getMyUserId()); Command command = new CommandAmf0("chat.sendMessage", null, chatMessage.encode()); handler.writeCommandExpectingResult(channel, command); } /** * {@link} https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/src/org/bigbluebutton/modules/chat/services/PrivateChatSharedObjectService.as#L103 * @param message * @param userid */ public void sendPrivateChatMessage(String message, String userid) { ChatMessage chatMessage = new ChatMessage(); chatMessage.setMessage(message); chatMessage.setUsername(handler.getContext().getJoinService().getJoinedMeeting().getFullname()); chatMessage.setUserId(handler.getContext().getMyUserId()); // \TODO the userId is being passed as Double, but should be passed as String on BigBlueButton 0.81 Command command = new CommandAmf0("chat.privateMessage", null, chatMessage.encode(), Double.valueOf(handler.getContext().getMyUserId()), Double.valueOf(userid)); handler.writeCommandExpectingResult(channel, command); // the message sent should be received like on public chat onPrivateChatMessage(chatMessage, handler.getContext().getUsersModule().getParticipants().get(userid)); } public void onPublicChatMessage(ChatMessage chatMessage) { IParticipant source = handler.getContext().getUsersModule().getParticipants().get(chatMessage.getUserId()); onPublicChatMessage(chatMessage, source); } public void onPublicChatMessage(ChatMessage chatMessage, IParticipant source) { for (OnPublicChatMessageListener listener : handler.getContext().getPublicChatMessageListeners()) listener.onPublicChatMessage(chatMessage, source); log.info("handling public chat message: {}", chatMessage); publicChatMessages.add(chatMessage); } public void onPrivateChatMessage(ChatMessage chatMessage, IParticipant source) { for (OnPrivateChatMessageListener listener : handler.getContext().getPrivateChatMessageListener()) listener.onPrivateChatMessage(chatMessage, source); synchronized (privateChatMessages) { if (!privateChatMessages.containsKey(source.getUserId())) privateChatMessages.put(source.getUserId(), new ArrayList<ChatMessage>()); privateChatMessages.get(source.getUserId()).add(chatMessage); } log.info("handling private chat message from {}: {}", source, chatMessage); } public List<ChatMessage> getPublicChatMessage() { return publicChatMessages; } public Map<String, List<ChatMessage>> getPrivateChatMessage() { return privateChatMessages; } @Override public boolean onCommand(String resultFor, Command command) { if (onGetChatMessages(resultFor, command)) return true; else return false; } }