summaryrefslogtreecommitdiffstats
path: root/Sluift
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2013-09-01 06:12:18 (GMT)
committerSwift Review <review@swift.im>2013-09-01 19:50:30 (GMT)
commit4f1274bcdd2af1a38b7de0b3ef4c18d6cd83c4a3 (patch)
treeeea00fbd093d746e862bc3e8fccadd98c7b0286b /Sluift
parent41bb9db24566f15d60d2522eaea6f00cbaabdf4a (diff)
downloadswift-contrib-4f1274bcdd2af1a38b7de0b3ef4c18d6cd83c4a3.zip
swift-contrib-4f1274bcdd2af1a38b7de0b3ef4c18d6cd83c4a3.tar.bz2
Sluift: More PubSub convenience methods & use cases.
- Convenience iterators to PubSub and PubSubNode. - Retrieving X most recent items - Retrieving a single item - Fixed GeoLocation serializer Change-Id: Ib4ecde225fb274b21163fcc9b52e19b0d3431860
Diffstat (limited to 'Sluift')
-rw-r--r--Sluift/ElementConvertors/BodyConvertor.cpp35
-rw-r--r--Sluift/ElementConvertors/BodyConvertor.h25
-rw-r--r--Sluift/ElementConvertors/DiscoItemsConvertor.cpp18
-rwxr-xr-xSluift/Examples/Wonderland.lua94
-rw-r--r--Sluift/LuaElementConvertors.cpp2
-rw-r--r--Sluift/SConscript1
-rw-r--r--Sluift/SluiftClient.cpp5
-rw-r--r--Sluift/SluiftClient.h4
-rw-r--r--Sluift/boot.lua68
-rw-r--r--Sluift/client.cpp53
10 files changed, 259 insertions, 46 deletions
diff --git a/Sluift/ElementConvertors/BodyConvertor.cpp b/Sluift/ElementConvertors/BodyConvertor.cpp
new file mode 100644
index 0000000..a7abc1e
--- /dev/null
+++ b/Sluift/ElementConvertors/BodyConvertor.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#include <Sluift/ElementConvertors/BodyConvertor.h>
+
+#include <lua.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Sluift/Lua/LuaUtils.h>
+
+using namespace Swift;
+
+BodyConvertor::BodyConvertor() : GenericLuaElementConvertor<Body>("body") {
+}
+
+BodyConvertor::~BodyConvertor() {
+}
+
+boost::shared_ptr<Body> BodyConvertor::doConvertFromLua(lua_State* L) {
+ boost::shared_ptr<Body> result = boost::make_shared<Body>();
+ if (boost::optional<std::string> value = Lua::getStringField(L, -1, "text")) {
+ result->setText(*value);
+ }
+ return result;
+}
+
+void BodyConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<Body> payload) {
+ lua_createtable(L, 0, 0);
+ if (!payload->getText().empty()) {
+ lua_pushstring(L, payload->getText().c_str());
+ lua_setfield(L, -2, "text");
+ }
+}
diff --git a/Sluift/ElementConvertors/BodyConvertor.h b/Sluift/ElementConvertors/BodyConvertor.h
new file mode 100644
index 0000000..b1cd494
--- /dev/null
+++ b/Sluift/ElementConvertors/BodyConvertor.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/Override.h>
+
+#include <Sluift/GenericLuaElementConvertor.h>
+#include <Swiften/Elements/Body.h>
+
+namespace Swift {
+ class LuaElementConvertors;
+
+ class BodyConvertor : public GenericLuaElementConvertor<Body> {
+ public:
+ BodyConvertor();
+ virtual ~BodyConvertor();
+
+ virtual boost::shared_ptr<Body> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE;
+ virtual void doConvertToLua(lua_State*, boost::shared_ptr<Body>) SWIFTEN_OVERRIDE;
+ };
+}
diff --git a/Sluift/ElementConvertors/DiscoItemsConvertor.cpp b/Sluift/ElementConvertors/DiscoItemsConvertor.cpp
index 6c39aea..a38e766 100644
--- a/Sluift/ElementConvertors/DiscoItemsConvertor.cpp
+++ b/Sluift/ElementConvertors/DiscoItemsConvertor.cpp
@@ -49,12 +49,18 @@ void DiscoItemsConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<DiscoIt
lua_createtable(L, boost::numeric_cast<int>(items.size()), 0);
for (size_t i = 0; i < items.size(); ++i) {
lua_createtable(L, 0, 0);
- lua_pushstring(L, items[i].getName().c_str());
- lua_setfield(L, -2, "name");
- lua_pushstring(L, items[i].getNode().c_str());
- lua_setfield(L, -2, "node");
- lua_pushstring(L, items[i].getJID().toString().c_str());
- lua_setfield(L, -2, "jid");
+ if (!items[i].getName().empty()) {
+ lua_pushstring(L, items[i].getName().c_str());
+ lua_setfield(L, -2, "name");
+ }
+ if (!items[i].getNode().empty()) {
+ lua_pushstring(L, items[i].getNode().c_str());
+ lua_setfield(L, -2, "node");
+ }
+ if (!items[i].getJID().isValid()) {
+ lua_pushstring(L, items[i].getJID().toString().c_str());
+ lua_setfield(L, -2, "jid");
+ }
lua_rawseti(L, -2, boost::numeric_cast<int>(i+1));
}
lua_setfield(L, -2, "items");
diff --git a/Sluift/Examples/Wonderland.lua b/Sluift/Examples/Wonderland.lua
index 235d8d0..426ec07 100755
--- a/Sluift/Examples/Wonderland.lua
+++ b/Sluift/Examples/Wonderland.lua
@@ -8,45 +8,97 @@
--
require "sluift"
---sluift.debug = true
+sluift.debug = true
characters = {
- {jid = "alice@wonderland.lit", name = "Alice", groups = {}, presence = "<presence/>"},
- {jid = "hatter@wonderland.lit", name = "Mad Hatter", groups = {}, presence = "<presence><show>away</show><status>At the Tea Party</status></presence>"},
- {jid ="queen@wonderland.lit", name = "Queen of Hearts", groups = {}, presence = "<presence><show>dnd</show><status>Executing</status></presence>"},
- {jid = "rabbit@wonderland.lit", name = "White Rabbit", groups = {"Animals"}, presence = "<presence><status>Oh dear!</status></presence>"},
- {jid = "turtle@wonderland.lit", name = "Mock Turtle", groups = {"Animals"}, presence = "<presence/>"},
+ ["Alice"] = {
+ jid = "alice@wonderland.lit", groups = {}, presence = "<presence/>"
+ },
+ ["Mad Hatter"] = {
+ jid = "hatter@wonderland.lit", groups = {},
+ presence = "<presence><show>away</show><status>At the Tea Party</status></presence>"
+ },
+ ["Queen of Hearts"] = {
+ jid ="queen@wonderland.lit", groups = {},
+ presence = "<presence><show>dnd</show><status>Executing</status></presence>"
+ },
+ ["White Rabbit"] = {
+ jid = "rabbit@wonderland.lit", groups = {"Animals"},
+ presence = "<presence><status>Oh dear!</status></presence>"},
+ ["Mock Turtle"] = {
+ jid = "turtle@wonderland.lit", groups = {"Animals"},
+ presence = "<presence/>"
+ },
}
-clients = {}
-for _, character in ipairs(characters) do
- print("Connecting " .. character["name"] .. "...")
- client = sluift.new_client(character["jid"], os.getenv("SLUIFT_PASS"))
+for name, character in pairs(characters) do
+ print("Connecting " .. name .. "...")
+ local client = sluift.new_client(character.jid, os.getenv("SLUIFT_PASS"))
client:set_options({compress = false, tls = false})
client:connect()
client:get_contacts()
- client:send(character["presence"])
- table.insert(clients, client)
- for _, contact in ipairs(characters) do
- if contact["jid"] ~= character["jid"] then
+ client:send(character.presence)
+ for contact_name, contact in pairs(characters) do
+ if contact.jid ~= character.jid then
client:add_contact(contact)
end
end
+ character.client = client
end
print("Confirming subscriptions")
-for _, client in ipairs(clients) do
- for _, contact in ipairs(characters) do
- client:confirm_subscription(contact["jid"])
+for _, character in pairs(characters) do
+ for _, contact in pairs(characters) do
+ character.client:confirm_subscription(contact.jid)
end
end
+print("Setting up PubSub nodes")
+local hatters_riddles = characters["Mad Hatter"].client:pubsub("pubsub.wonderland.lit"):node("hatters_riddles")
+hatters_riddles:delete()
+assert(hatters_riddles:create())
+
+local queen_quotes = characters["Queen of Hearts"].client:pubsub("pubsub.wonderland.lit"):node("queen_quotes")
+queen_quotes:delete()
+assert(queen_quotes:create())
+queen_quotes:publish{id = 'quote1', item = {_type = 'body', text = 'Off with his head!'}}
+queen_quotes:publish{id = 'quote2', item = {_type = 'body', text = 'Off with her head!'}}
+queen_quotes:publish{id = 'quote3', item = {_type = 'body', text = 'Off with their heads!'}}
+
+characters['Mad Hatter'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{
+ item = {_type = 'user_location', latitude = 50.376739, longitude = -4.200709}}
+characters['Queen of Hearts'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{
+ item = {_type = 'user_location', latitude = 50.551123, longitude = -4.141654}}
+characters['Mock Turtle'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{
+ item = {_type = 'user_location', latitude = 50.366630, longitude = -4.134518}}
+characters['White Rabbit'].client:pubsub():node('http://jabber.org/protocol/geoloc'):publish{
+ item = {_type = 'user_location', latitude = 50.332907, longitude = -4.759194}}
+
+
+
+print("Disconnecting alice")
+characters['Alice'].client:disconnect()
+
print("Done. Waiting ...")
while true do
- for _, client in ipairs(clients) do
- for message in client:messages {timeout = 1000} do
- client:send_message{to = e["from"], body = "Off with their heads!"}
+ for name, character in pairs(characters) do
+ if name == 'Queen of Hearts' then
+ for message in character.client:messages{timeout = 1000} do
+ if message.body == 'publish' then
+ queen_quotes:publish{item = {_type = 'body', text = 'Off with her head!'}}
+ queen_quotes:publish{item = {_type = 'body', text = 'Off with his head!'}}
+ else
+ character.client:send_message{to = e["from"], body = "Off with their heads!"}
+ end
+ end
+ elseif name == "Mad Hatter" then
+ for message in character.client:messages{timeout = 1000} do
+ if message.body == 'publish' then
+ hatters_riddles:publish{item = {_type = 'body', text = 'Why is a raven like a writing desk?'}}
+ end
+ end
+ else
+ for message in character.client:messages{timeout = 100} do end
end
end
- sluift.sleep(1000)
end
diff --git a/Sluift/LuaElementConvertors.cpp b/Sluift/LuaElementConvertors.cpp
index 5913858..cadfbc4 100644
--- a/Sluift/LuaElementConvertors.cpp
+++ b/Sluift/LuaElementConvertors.cpp
@@ -20,6 +20,7 @@
#include <Sluift/ElementConvertors/SoftwareVersionConvertor.h>
#include <Sluift/ElementConvertors/VCardUpdateConvertor.h>
#include <Sluift/ElementConvertors/VCardConvertor.h>
+#include <Sluift/ElementConvertors/BodyConvertor.h>
#include <Sluift/Lua/LuaUtils.h>
#include <Sluift/Lua/Exception.h>
@@ -28,6 +29,7 @@ using namespace Swift;
LuaElementConvertors::LuaElementConvertors() {
registerConvertors();
convertors.push_back(boost::make_shared<PubSubEventConvertor>(this));
+ convertors.push_back(boost::make_shared<BodyConvertor>());
convertors.push_back(boost::make_shared<VCardConvertor>());
convertors.push_back(boost::make_shared<VCardUpdateConvertor>());
convertors.push_back(boost::make_shared<FormConvertor>());
diff --git a/Sluift/SConscript b/Sluift/SConscript
index fcc39fc..c4d3b28 100644
--- a/Sluift/SConscript
+++ b/Sluift/SConscript
@@ -18,6 +18,7 @@ elif env["SCONS_STAGE"] == "build" :
"LuaElementConvertors.cpp",
"LuaElementConvertor.cpp",
"Response.cpp",
+ "ElementConvertors/BodyConvertor.cpp",
"ElementConvertors/VCardUpdateConvertor.cpp",
"ElementConvertors/PubSubEventConvertor.cpp",
"ElementConvertors/RawXMLElementConvertor.cpp",
diff --git a/Sluift/SluiftClient.cpp b/Sluift/SluiftClient.cpp
index 726a683..8a8d772 100644
--- a/Sluift/SluiftClient.cpp
+++ b/Sluift/SluiftClient.cpp
@@ -87,14 +87,15 @@ void SluiftClient::setSoftwareVersion(const std::string& name, const std::string
client->setSoftwareVersion(name, version, os);
}
-boost::optional<SluiftClient::Event> SluiftClient::getNextEvent(boost::optional<Event::Type> type, int timeout) {
+boost::optional<SluiftClient::Event> SluiftClient::getNextEvent(
+ int timeout, boost::function<bool (const Event&)> condition) {
Watchdog watchdog(timeout, networkFactories->getTimerFactory());
while (true) {
// Look for pending events in the queue
while (!pendingEvents.empty()) {
Event event = pendingEvents.front();
pendingEvents.pop_front();
- if (!type || *type == event.type) {
+ if (!condition || condition(event)) {
return event;
}
}
diff --git a/Sluift/SluiftClient.h b/Sluift/SluiftClient.h
index 6515b7d..bedd6ba 100644
--- a/Sluift/SluiftClient.h
+++ b/Sluift/SluiftClient.h
@@ -9,6 +9,7 @@
#include <deque>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
+#include <boost/function.hpp>
#include <Swiften/Client/ClientOptions.h>
#include <Sluift/globals.h>
@@ -101,7 +102,8 @@ namespace Swift {
void disconnect();
void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os);
- boost::optional<SluiftClient::Event> getNextEvent(boost::optional<Event::Type> type, int timeout);
+ boost::optional<SluiftClient::Event> getNextEvent(int timeout,
+ boost::function<bool (const Event&)> condition = 0);
std::vector<XMPPRosterItem> getRoster();
private:
diff --git a/Sluift/boot.lua b/Sluift/boot.lua
index ae8cc41..e81257a 100644
--- a/Sluift/boot.lua
+++ b/Sluift/boot.lua
@@ -157,6 +157,12 @@ for method, event_type in pairs({message = 'message', presence = 'presence', pub
options['type'] = event_type
return client:for_each_event (options)
end
+
+ Client['get_next_' .. method] = function (client, ...)
+ local options = parse_options({}, ...)
+ options['type'] = event_type
+ return client:get_next_event(options)
+ end
end
for method, event_type in pairs({messages = 'message', pubsub_events = 'pubsub'}) do
@@ -222,6 +228,16 @@ for method, query_type in pairs(simple_pubsub_queries) do
end
end
+for _, method in ipairs({'events', 'get_next_event', 'for_each_event'}) do
+ PubSub[method] = function (node, ...)
+ local options = parse_options({}, ...)
+ options['if'] = function (event)
+ return event.type == 'pubsub' and event.from == node.jid and event.node == node
+ end
+ return node.client[method](node.client, options)
+ end
+end
+
--------------------------------------------------------------------------------
-- PubSubNode
--------------------------------------------------------------------------------
@@ -247,7 +263,6 @@ local simple_pubsub_node_queries = {
get_configuration = 'pubsub_owner_configure',
get_subscriptions = 'pubsub_subscriptions',
get_affiliations = 'pubsub_affiliations',
- get_items = 'pubsub_items',
get_default_subscription_options = 'pubsub_default',
}
for method, query_type in pairs(simple_pubsub_node_queries) do
@@ -259,6 +274,23 @@ for method, query_type in pairs(simple_pubsub_node_queries) do
end
end
+function PubSubNode.get_items (node, ...)
+ local options = parse_options({}, ...)
+ local items = options.items or {}
+ if options.maximum_items then
+ items = merge_tables({maximum_items = options.maximum_items}, items)
+ end
+ items = merge_tables({_type = 'pubsub_items', node = node.node}, items)
+ return node.client:query_pubsub(merge_tables({
+ type = 'get', to = node.jid, query = items}, options))
+end
+
+function PubSubNode.get_item (node, ...)
+ local options = parse_options({}, ...)
+ if not type(options.id) == 'string' then error('Expected ID') end
+ return PubSubNode.get_items(node, {items = {{id = options.id}}})
+end
+
function PubSubNode.create (node, options)
options = options or {}
local configure
@@ -291,11 +323,12 @@ function PubSubNode.set_configuration(node, options)
}, options))
end
-function PubSubNode.subscribe(node, options)
- options = options or {}
+function PubSubNode.subscribe(node, ...)
+ local options = parse_options(...)
+ local jid = options.jid or sluift.jid.to_bare(node.client:jid())
return node.client:query_pubsub(merge_tables(
{ type = 'set', to = node.jid, query = {
- _type = 'pubsub_subscribe', node = node.node, jid = options['jid'] }
+ _type = 'pubsub_subscribe', node = node.node, jid = jid }
}, options))
end
@@ -332,13 +365,11 @@ function PubSubNode.publish(node, ...)
}, options))
end
-function PubSubNode.retract(node, options)
- options = options or {}
- local item_ids = options['items']
- item_ids = item_ids or { options['item'] }
- local items = {}
- for _, item_id in ipairs(item_ids) do
- items[#items+1] = { id = item_id }
+function PubSubNode.retract(node, ...)
+ local options = parse_options({}, ...)
+ local items = options.items
+ if options.id then
+ items = {{id = options.id}}
end
return node.client:query_pubsub(merge_tables(
{ type = 'set', to = node.jid, query = {
@@ -346,14 +377,25 @@ function PubSubNode.retract(node, options)
}}, options))
end
-function PubSubNode.purge(node, options)
- options = options or {}
+function PubSubNode.purge(node, ...)
+ local options = parse_options({}, ...)
return node.client:query_pubsub(merge_tables(
{ type = 'set', to = node.jid, query = {
_type = 'pubsub_owner_purge', node = node.node
}}, options))
end
+-- Iterators over events
+for _, method in ipairs({'events', 'get_next_event', 'for_each_event'}) do
+ PubSubNode[method] = function (node, ...)
+ local options = parse_options({}, ...)
+ options['if'] = function (event)
+ return event.type == 'pubsub' and event.from == node.jid and event.node == node.node
+ end
+ return node.client[method](node.client, options)
+ end
+end
+
--------------------------------------------------------------------------------
-- Service discovery
--------------------------------------------------------------------------------
diff --git a/Sluift/client.cpp b/Sluift/client.cpp
index 6d8bee8..df43075 100644
--- a/Sluift/client.cpp
+++ b/Sluift/client.cpp
@@ -4,6 +4,11 @@
* See the COPYING file for more information.
*/
+#include <boost/lambda/lambda.hpp>
+#include <boost/lambda/bind.hpp>
+#include <boost/assign/list_of.hpp>
+#include <iostream>
+
#include <Sluift/SluiftClient.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Elements/SoftwareVersion.h>
@@ -20,7 +25,6 @@
#include <Swiften/Roster/SetRosterRequest.h>
#include <Swiften/Presence/SubscriptionManager.h>
#include <Swiften/Roster/XMPPRosterItem.h>
-#include <boost/assign/list_of.hpp>
#include <Sluift/Watchdog.h>
#include <Swiften/Queries/Requests/GetSoftwareVersionRequest.h>
#include <Sluift/Lua/FunctionRegistration.h>
@@ -30,9 +34,9 @@
#include <Sluift/Lua/Exception.h>
#include <Sluift/Lua/LuaUtils.h>
#include <Sluift/globals.h>
-#include <iostream>
using namespace Swift;
+namespace lambda = boost::lambda;
static const std::string SLUIFT_CLIENT = Lua::FunctionRegistry::getMetaTableNameForType("Client");
@@ -376,12 +380,33 @@ static void pushEvent(lua_State* L, const SluiftClient::Event& event) {
}
}
+struct CallUnaryLuaPredicateOnEvent {
+ CallUnaryLuaPredicateOnEvent(lua_State* L, int index) : L(L), index(index) {
+ }
+
+ bool operator()(const SluiftClient::Event& event) {
+ lua_pushvalue(L, index);
+ pushEvent(L, event);
+ if (lua_pcall(L, 1, 1, 0) != 0) {
+ throw Lua::Exception(lua_tostring(L, -1));
+ }
+ bool result = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return result;
+ }
+
+ lua_State* L;
+ int index;
+};
+
+
SLUIFT_LUA_FUNCTION(Client, get_next_event) {
Sluift::globals.eventLoop.runOnce();
SluiftClient* client = getClient(L);
int timeout = Sluift::globals.timeout;
boost::optional<SluiftClient::Event::Type> type;
+ int condition = 0;
if (lua_istable(L, 2)) {
if (boost::optional<std::string> typeString = Lua::getStringField(L, 2, "type")) {
if (*typeString == "message") {
@@ -397,9 +422,25 @@ SLUIFT_LUA_FUNCTION(Client, get_next_event) {
if (boost::optional<int> timeoutInt = Lua::getIntField(L, 2, "timeout")) {
timeout = *timeoutInt;
}
+ lua_getfield(L, 2, "if");
+ if (lua_isfunction(L, -1)) {
+ condition = Lua::absoluteOffset(L, -1);
+ }
}
- if (boost::optional<SluiftClient::Event> event = client->getNextEvent(type, timeout)) {
+ boost::optional<SluiftClient::Event> event;
+ if (condition) {
+ event = client->getNextEvent(timeout, CallUnaryLuaPredicateOnEvent(L, condition));
+ }
+ else if (type) {
+ event = client->getNextEvent(
+ timeout, lambda::bind(&SluiftClient::Event::type, lambda::_1) == *type);
+ }
+ else {
+ event = client->getNextEvent(timeout);
+ }
+
+ if (event) {
pushEvent(L, *event);
}
else {
@@ -510,6 +551,12 @@ SLUIFT_LUA_FUNCTION(Client, set_caps_node) {
return 0;
}
+SLUIFT_LUA_FUNCTION(Client, jid) {
+ SluiftClient* client = getClient(L);
+ lua_pushstring(L, client->getClient()->getJID().toString().c_str());
+ return 1;
+}
+
SLUIFT_LUA_FUNCTION(Client, __gc) {
SluiftClient* client = getClient(L);
delete client;