/******************************************************************************* * Copyright (c) 2013 Hani Naguib. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Hani Naguib - initial API and implementation ******************************************************************************/ package com.gvmax.server; import java.io.IOException; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import com.codahale.metrics.Timer.Context; import com.google.gdata.data.contacts.ContactEntry; import com.google.gdata.data.extensions.Email; import com.google.gdata.data.extensions.Name; import com.google.gdata.data.extensions.PhoneNumber; import com.gvmax.common.model.User; import com.gvmax.common.model.XMPPAction; import com.gvmax.common.relay.GVMaxRelay; import com.gvmax.common.util.EmailUtils; import com.gvmax.common.util.MetricsUtil; import com.gvmax.common.util.MiscUtils; import com.gvmax.common.util.PhoneUtil; import com.gvmax.common.util.TimeTrack; import com.gvmax.data.queue.QueueListener.QueueProcessor; import com.gvmax.data.user.UserDAO; import com.gvmax.google.talk.GTalk; import com.gvmax.google.voice.GoogleVoice; import com.gvmax.google.voice.GoogleVoicePhone; import com.gvmax.server.util.ContactUtil; import com.gvmax.server.util.Notifier; public class XMPPReceiver implements QueueProcessor<XMPPAction> { private static final Logger logger = Logger.getLogger(XMPPReceiver.class); private UserDAO userDAO; private GVMaxRelay relay; public XMPPReceiver(UserDAO userDAO, GVMaxRelay relay) { this.userDAO = userDAO; this.relay = relay; } @Override public void process(XMPPAction action) { MetricsUtil.getCounter(XMPPReceiver.class, "received").inc(); Context timer = MetricsUtil.getTimer(XMPPReceiver.class, "processTime").time(); TimeTrack tt = new TimeTrack(); try { tt.mark("received.time", action.getTimestamp()); tt.mark("process.time"); User user = extractInfo(action); XMPPProcessor processor = new XMPPProcessor(user, action); if (user == null) { MetricsUtil.getCounter(XMPPReceiver.class, "unregistered").inc(); processor.sendMessage("You are not a registered gvmax user. Action cancelled"); return; } userDAO.incrementGTalkCount(user.getEmail()); if (!processor.processCommands()) { if (!user.isGvPassword()) { processor.sendMessage("google voice password required to send SMS replies"); return; } if ("unknown".equals(action.getBotNumber())) { processor.sendMessage("cannot send sms to unknown number"); return; } try { Notifier notifier = new Notifier(user, userDAO, null); int num = notifier.sendSMS(action.getBotNumber(), action.getMessage()); if (num > 1) { processor.sendMessage("Sending SMS to " + num + " numbers"); } } catch (Exception e) { processor.sendMessage("Failed to send SMS error = " + e.getMessage()); } } } finally { timer.stop(); tt.mark("end.time"); logger.info("XMPP_IN ProcessTime: " + tt.deltaInSecs() + ", total: " + tt.elapsedInSecs()); } } private User extractInfo(XMPPAction action) { String[] to = action.getTo(); String botJId = to[0].substring(6, to[0].indexOf('>')); String botNumber = botJId.substring(0, botJId.indexOf('@')); String senderJId = action.getFrom().substring(6, action.getFrom().indexOf('>')); String gtalkEmail = EmailUtils.formatEmail(action.getFrom().substring(6, action.getFrom().indexOf('/'))); action.setBotJId(botJId); action.setBotNumber(botNumber); action.setSenderJId(senderJId); return userDAO.retrieveByGTalk(gtalkEmail); } class XMPPProcessor { private User user; private XMPPAction action; public XMPPProcessor(User user, XMPPAction action) { this.user = user; this.action = action; } private boolean processCommands() { String cmd = action.getMessage().trim().toLowerCase(); if (COMMAND_HELP.equals(cmd)) { help(); return true; } if (COMMAND_INFO.equals(cmd)) { info(); return true; } if (cmd.startsWith(COMMAND_ADDNUMBER)) { return addNumber(); } if (COMMAND_PHONES.equals(cmd)) { displayPhones(); return true; } if (cmd.startsWith(COMMAND_PHONES)) { return setFwdPhone(); } if (COMMAND_CALL.equals(cmd)) { callNumber(action.getBotNumber()); return true; } if (cmd.startsWith(COMMAND_CALL)) { String[] args = cmd.split(" "); if (args.length > 1) { callNumber(restOf(args)); return true; } } if (COMMAND_GROUPS.equals(cmd)) { showGroups(); return true; } if (cmd.startsWith(COMMAND_GROUPLIST)) { String[] args = cmd.split(" "); if (args.length == 2) { try { int index = Integer.parseInt(args[1]); listGroup(index); return true; } catch (Exception e) { MiscUtils.emptyBlock(); } } } if (cmd.startsWith(COMMAND_GROUPS)) { String[] args = cmd.split(" "); if (args.length == 2) { try { int index = Integer.parseInt(args[1]); addGroup(index); return true; } catch (Exception e) { MiscUtils.emptyBlock(); } } } return false; } // --------------------- // COMMAND METHODS // --------------------- private void help() { StringBuffer msg = new StringBuffer("HELP\n"); msg.append("gg - Displays this help text.\n"); msg.append("ggi - Displays information about buddy.\n"); msg.append("gga {number} - Adds a new phone number to your IM roster.\n"); msg.append("ggp - Displays list of available fowarding phones.\n"); msg.append("ggp {index} - Selects new fowarding number.\n"); msg.append("ggc - Calls buddy via GoogleVoice.\n"); msg.append("ggc {number} - Calls a number via GoogleVoice.\n"); msg.append("ggr - Show list of groups.\n"); msg.append("ggr {index} - Add group to IM roster.\n"); msg.append("ggrl {index} - List group members.\n"); sendMessage(msg.toString()); } private void info() { sendMessage("retrieving info for " + action.getBotNumber() + "..."); ContactEntry entry = ContactUtil.getContactEntry(user, action.getBotNumber()); StringBuffer msg = new StringBuffer(); if (entry != null) { if (entry.hasName()) { Name name = entry.getName(); if (name.hasFullName()) { String fullNameToDisplay = name.getFullName().getValue(); if (name.getFullName().hasYomi()) { fullNameToDisplay += " (" + name.getFullName().getYomi() + ")"; } msg.append("Name: " + fullNameToDisplay + "\n"); } } if (entry.getEmailAddresses().size() > 0) { msg.append("Email Addresses:\n"); for (Email email : entry.getEmailAddresses()) { msg.append(" " + email.getAddress() + "\n"); } } if (entry.getPhoneNumbers().size() > 0) { msg.append("Phone Numbers:\n"); for (PhoneNumber number : entry.getPhoneNumbers()) { msg.append(" "); msg.append(PhoneUtil.normalizeNumber(number.getPhoneNumber())); if (number.getLabel() != null) { msg.append(" [" + number.getLabel().substring(number.getLabel().lastIndexOf('#') + 1) + "]"); } if (number.getRel() != null) { msg.append(" [" + number.getRel().substring(number.getRel().lastIndexOf('#') + 1) + "]"); } msg.append("\n"); } } } else { msg.append("Contact information for " + action.getBotNumber() + " could not retrieved."); } sendMessage(msg.toString()); } private boolean addNumber() { String[] info = action.getMessage().split(" "); if (info.length <= 1) { return false; } String number = PhoneUtil.normalizeNumber(restOf(info)); if (!PhoneUtil.isNumber(number)) { return false; } sendMessage("Adding buddy " + number); String nickname = ContactUtil.getContact(user, number); if (nickname == null) { nickname = number; } try { GTalk.addBuddy(user.getgTalkEmail(), user.getgTalkPassword(), number + "@" + relay.getAppName() + ".appspotchat.com", nickname, user.getgTalkGroup()); } catch (IOException e) { sendMessage("Unable to add number " + number + " : " + e.getMessage()); } sendMessage("Added " + number + " as " + nickname); return true; } private void displayPhones() { if (!user.isGvPassword()) { sendMessage("google voice password required for this functionality"); return; } sendMessage("retrieving available forward phones..."); StringBuffer msg = new StringBuffer(); try { GoogleVoice gv = new GoogleVoice(user.getEmail(), user.getPassword()); List<GoogleVoicePhone> phones = gv.getPhones(); if (phones.size() == 0) { msg.append("No phones have been registered with your GoogleVoice account"); } else { int pn = 1; for (GoogleVoicePhone phone : phones) { if (PhoneUtil.numbersMatch(phone.getNumber(), user.getGvFwdPhone())) { msg.append(pn + ". ** " + phone.getName() + " " + PhoneUtil.normalizeNumber(phone.getNumber()) + " **\n"); } else { msg.append(pn + ". " + phone.getName() + " " + PhoneUtil.normalizeNumber(phone.getNumber()) + "\n"); } pn += 1; } } sendMessage(msg.toString()); } catch (Exception e) { sendMessage("Unable to retrieve phones : " + e.getMessage()); } } private boolean setFwdPhone() { if (!user.isGvPassword()) { sendMessage("google voice password required for this functionality"); return true; } String[] info = action.getMessage().split(" "); if (info.length != 2) { return false; } int index = -1; try { index = Integer.parseInt(info[1]); } catch (Exception e) { return false; } try { GoogleVoice gv = new GoogleVoice(user.getEmail(), user.getPassword()); List<GoogleVoicePhone> phones = gv.getPhones(); if (phones.size() == 0) { sendMessage("No phones have been registered with your GoogleVoice account"); } else { if (index <= 0) { sendMessage("Invalid phone index '" + index + "' please select an index from 1 to " + phones.size()); return true; } if (phones.size() < index) { sendMessage("invalid phone index '" + index + "' please select an index from 1 to " + phones.size()); displayPhones(); return true; } GoogleVoicePhone phone = phones.get(index - 1); // HANI new API userDAO.setGVFwdPhone(user.getEmail(), phone.getNumber(), phone.getType()); sendMessage("Foward phone set to : " + phone.getName()); } } catch (Exception e) { sendMessage("Unable to select phone: " + e.getMessage()); } return true; } private void callNumber(String number) { if (!user.isGvPassword()) { sendMessage("google voice password required for this functionality"); return; } if (user.getGvFwdPhone() == null) { sendMessage("forwarding phone not selected, please select phone via ggp command"); return; } number = PhoneUtil.normalizeNumber(number); if (!PhoneUtil.isNumber(number)) { sendMessage(number + " is not valid"); return; } GoogleVoice gv = new GoogleVoice(user.getEmail(), user.getPassword()); sendMessage("Placing call to " + number + ", " + user.getGvFwdPhone() + " set as fowarding phone"); try { gv.call(number, user.getGvFwdPhone()); } catch (IOException e) { sendMessage("Call failed : " + e.getMessage()); } } private void showGroups() { sendMessage("retrieving list of groups"); try { List<String> groups = ContactUtil.getGroups(user); StringBuffer msg = new StringBuffer("GROUPS\n"); int index = 1; for (String group : groups) { msg.append(" " + index + ". " + group + "\n"); index += 1; } if (groups.size() > 0) { sendMessage(msg.toString()); } else { sendMessage("no GVMax compatible group found"); } } catch (IOException e) { sendMessage("Unable to retrieve groups : " + e.getMessage()); } } private void addGroup(int index) { try { sendMessage("adding group..."); List<String> groups = ContactUtil.getGroups(user); if (index <= 0) { sendMessage("Invalid group index '" + index + "' please select an index from 1 to " + groups.size()); showGroups(); return; } if (groups.size() < index) { sendMessage("invalid group index '" + index + "' please select an index from 1 to " + groups.size()); showGroups(); return; } String group = groups.get(index - 1); String g = group; g = g.replaceAll(" ", "_"); g = g.replaceAll(":", "."); GTalk.addBuddy(user.getgTalkEmail(), user.getgTalkPassword(), g + "@" + relay.getAppName() + ".appspotchat.com", group, user.getgTalkGroup()); sendMessage("group " + group + " added"); } catch (IOException e) { sendMessage("unable to add group : " + e.getMessage()); } } private void listGroup(int index) { sendMessage("retrieving group members"); try { List<String> groups = ContactUtil.getGroups(user); if (index <= 0) { sendMessage("Invalid group index '" + index + "' please select an index from 1 to " + groups.size()); showGroups(); return; } if (groups.size() < index) { sendMessage("invalid group index '" + index + "' please select an index from 1 to " + groups.size()); showGroups(); return; } String group = groups.get(index - 1); Map<String, String> contacts = ContactUtil.getNumbersInGroup(user, group); if (contacts.size() == 0) { sendMessage("group " + group + " has no members"); return; } StringBuffer msg = new StringBuffer("Contacts for " + group + "\n"); for (Map.Entry<String, String> contact : contacts.entrySet()) { msg.append(contact.getKey() + " : " + contact.getValue() + "\n"); } msg.append("Total : "); msg.append(contacts.size()); sendMessage(msg.toString()); } catch (IOException e) { sendMessage("Unable to get group contacts: " + e.getMessage()); } } // -------------------------- // UTIL // -------------------------- String restOf(String[] args) { StringBuffer retVal = new StringBuffer(args[1]); for (int x = 2; x < args.length; x++) { retVal.append(" "); retVal.append(args[x]); } return retVal.toString(); } private void sendMessage(String message) { try { relay.sendXmppMessage(action.getBotJId(), action.getSenderJId(), message); if (user != null) { userDAO.incrementGTalkCount(user.getEmail()); } } catch (IOException e) { MiscUtils.emptyBlock(); } } } // ----------------- // COMMANDS // ----------------- private static final String COMMAND_HELP = "gg"; private static final String COMMAND_INFO = "ggi"; private static final String COMMAND_ADDNUMBER = "gga"; private static final String COMMAND_PHONES = "ggp"; private static final String COMMAND_CALL = "ggc"; private static final String COMMAND_GROUPS = "ggr"; private static final String COMMAND_GROUPLIST = "ggrl"; }