Index: binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatCommandHandler.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatCommandHandler.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatCommandHandler.js @@ -5,10 +5,11 @@ */ class ChatCommandHandler { - constructor(chatMessagesPanel, systemMessageFormat) + constructor(chatMessagesPanel, systemMessageFormat, privateSenderFormat) { this.chatMessagesPanel = chatMessagesPanel; this.systemMessageFormat = systemMessageFormat; + this.privateSenderFormat = privateSenderFormat; } /** @@ -97,6 +98,23 @@ return true; } }, + "private": { + "description": translate("Send private message to user. Usage: /private nick message"), + "handler": function(args) { + let index = args.indexOf(" "); + if (index != -1) + { + let target = args.substr(0, index); + let msg = args.substr(index + 1); + Engine.PrivateMessage(target, msg); + this.chatMessagesPanel.addText( + Date.now() / 1000, + this.privateSenderFormat.format(g_Nickname, target, msg)); + return true; + } + return false; + } + }, "kick": { "description": translate("Kick a specified user from the lobby. Usage: /kick nick reason"), "handler": function(args) { Index: binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatInputPanel.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatInputPanel.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatInputPanel.js @@ -4,9 +4,9 @@ */ class ChatInputPanel { - constructor(xmppMessages, chatMessagesPanel, systemMessageFormat) + constructor(xmppMessages, chatMessagesPanel, systemMessageFormat, privateSenderFormat) { - this.chatCommandHandler = new ChatCommandHandler(chatMessagesPanel, systemMessageFormat); + this.chatCommandHandler = new ChatCommandHandler(chatMessagesPanel, systemMessageFormat, privateSenderFormat); this.chatSubmit = Engine.GetGUIObjectByName("chatSubmit"); this.chatSubmit.onPress = this.submitChatInput.bind(this); Index: binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatMessages/ChatMessageEvents.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatMessages/ChatMessageEvents.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatMessages/ChatMessageEvents.js @@ -19,7 +19,7 @@ onPrivateMessage(message) { // We intend to not support private messages between users - if ((!message.from && message.text.length > 0) || Engine.LobbyGetPlayerRole(message.from) == "moderator") + // if ((!message.from && message.text.length > 0) || Engine.LobbyGetPlayerRole(message.from) == "moderator") // some XMPP clients send trailing whitespace this.chatMessagesPanel.addText(message.time, this.chatMessageFormat.format(message)); } Index: binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatPanel.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatPanel.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatPanel.js @@ -14,9 +14,10 @@ { this.systemMessageFormat = new SystemMessageFormat(); this.statusMessageFormat = new StatusMessageFormat(); + this.privateSenderFormat = new PrivateSenderFormat(); this.chatMessagesPanel = new ChatMessagesPanel(xmppMessages); - this.chatInputPanel = new ChatInputPanel(xmppMessages, this.chatMessagesPanel, this.systemMessageFormat); + this.chatInputPanel = new ChatInputPanel(xmppMessages, this.chatMessagesPanel, this.systemMessageFormat, this.privateSenderFormat); this.chatMessageEvents = {}; for (let name in ChatMessageEvents) Index: source/lobby/IXmppClient.h =================================================================== --- source/lobby/IXmppClient.h +++ source/lobby/IXmppClient.h @@ -54,7 +54,7 @@ virtual JS::Value GUIGetGameList(const ScriptRequest& rq) = 0; virtual JS::Value GUIGetBoardList(const ScriptRequest& rq) = 0; virtual JS::Value GUIGetProfile(const ScriptRequest& rq) = 0; - + virtual void SendPrivateMessage(const ScriptInterface& guiInterface, const std::string& username, const std::string& msg) = 0; virtual JS::Value GuiPollNewMessages(const ScriptInterface& guiInterface) = 0; virtual JS::Value GuiPollHistoricMessages(const ScriptInterface& guiInterface) = 0; virtual bool GuiPollHasPlayerListUpdate() = 0; Index: source/lobby/XmppClient.h =================================================================== --- source/lobby/XmppClient.h +++ source/lobby/XmppClient.h @@ -161,6 +161,7 @@ public: JS::Value GuiPollNewMessages(const ScriptInterface& guiInterface); JS::Value GuiPollHistoricMessages(const ScriptInterface& guiInterface); + void SendPrivateMessage(const ScriptInterface& guiInterface, const std::string& username, const std::string& msg); bool GuiPollHasPlayerListUpdate(); void SendMUCMessage(const std::string& message); Index: source/lobby/XmppClient.cpp =================================================================== --- source/lobby/XmppClient.cpp +++ source/lobby/XmppClient.cpp @@ -723,6 +723,17 @@ return hasUpdate; } +void XmppClient::SendPrivateMessage(const ScriptInterface& guiInterface, const std::string& username, const std::string& msg) +{ + if ((m_isConnected && !m_initialLoadComplete)) + return; + + glooxwrapper::JID targetJID(m_room + "@conference." + m_server + "/" + username); + glooxwrapper::MessageSession session = glooxwrapper::MessageSession(m_client, targetJID); + session.send(msg); + m_client->disposeMessageSession(session); +} + JS::Value XmppClient::GuiPollNewMessages(const ScriptInterface& guiInterface) { if ((m_isConnected && !m_initialLoadComplete) || m_GuiMessageQueue.empty()) Index: source/lobby/glooxwrapper/glooxwrapper.h =================================================================== --- source/lobby/glooxwrapper/glooxwrapper.h +++ source/lobby/glooxwrapper/glooxwrapper.h @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -338,7 +339,6 @@ JID* alternate; }; - class GLOOXWRAPPER_API ConnectionListener { public: @@ -399,7 +399,6 @@ int m_extensionType; }; - class GLOOXWRAPPER_API Client { NONCOPYABLE(Client); @@ -436,6 +435,8 @@ void setPresence(gloox::Presence::PresenceType pres, int priority, const string& status = ""); void disconnect(); + + void disposeMessageSession(MessageSession&); }; class GLOOXWRAPPER_API DelayedDelivery @@ -528,6 +529,19 @@ const glooxwrapper::DelayedDelivery* when() const; }; + class GLOOXWRAPPER_API MessageSession + { + NONCOPYABLE(MessageSession); + private: + gloox::MessageSession* m_Wrapped; + + public: + gloox::MessageSession* getWrapped() { return m_Wrapped; } + MessageSession(Client* parent, const JID& jid, bool wantUpgrade = false, int types = 0, bool honorTID = true); + ~MessageSession(); + void send(const std::string& message); + }; + class GLOOXWRAPPER_API MUCRoom { NONCOPYABLE(MUCRoom); Index: source/lobby/glooxwrapper/glooxwrapper.cpp =================================================================== --- source/lobby/glooxwrapper/glooxwrapper.cpp +++ source/lobby/glooxwrapper/glooxwrapper.cpp @@ -364,6 +364,12 @@ m_Wrapped->send(iq.getWrapped()); } +void glooxwrapper::Client::disposeMessageSession(glooxwrapper::MessageSession& messageSession) +{ + m_Wrapped->disposeMessageSession(messageSession.getWrapped()); + messageSession.~MessageSession(); +} + void glooxwrapper::Client::setTls(gloox::TLSPolicy tls) { m_Wrapped->setTls(tls); @@ -602,6 +608,22 @@ delete m_HandlerWrapper; } +glooxwrapper::MessageSession::MessageSession(Client* parent, const JID& jid, bool wantUpgrade, int types, bool honorTID) +{ + m_Wrapped = new gloox::MessageSession(parent->getWrapped(), jid.getWrapped(), false, 0, false); +} + +glooxwrapper::MessageSession::~MessageSession() +{ + // Do not delete gloox::MessageSession manually + // ClientBase::disposeMessageSession() will handle that +} + +void glooxwrapper::MessageSession::send(const std::string& message) +{ + m_Wrapped->send(message); +} + const glooxwrapper::string glooxwrapper::MUCRoom::nick() const { return m_Wrapped->nick(); Index: source/lobby/scripting/JSInterface_Lobby.cpp =================================================================== --- source/lobby/scripting/JSInterface_Lobby.cpp +++ source/lobby/scripting/JSInterface_Lobby.cpp @@ -138,6 +138,13 @@ return g_XmppClient->GuiPollNewMessages(scriptInterface); } +void PrivateMessage(const ScriptInterface& scriptInterface, const std::string& username, const std::string& msg) +{ + if (!g_XmppClient) + return; + + g_XmppClient->SendPrivateMessage(scriptInterface, username, msg); +} // Non-public secure PBKDF2 hash function with salting and 1,337 iterations // @@ -219,6 +226,7 @@ REGISTER_XMPP(GetNick, "LobbyGetNick"); REGISTER_XMPP(GetJID, "LobbyGetJID"); REGISTER_XMPP(kick, "LobbyKick"); + ScriptFunction::Register<&PrivateMessage>(rq, "PrivateMessage"); REGISTER_XMPP(ban, "LobbyBan"); REGISTER_XMPP(GetPresence, "LobbyGetPlayerPresence"); REGISTER_XMPP(GetRole, "LobbyGetPlayerRole");