diff options
author | Remko Tronçon <git@el-tramo.be> | 2013-09-01 06:12:18 (GMT) |
---|---|---|
committer | Swift Review <review@swift.im> | 2013-09-01 19:50:30 (GMT) |
commit | 4f1274bcdd2af1a38b7de0b3ef4c18d6cd83c4a3 (patch) | |
tree | eea00fbd093d746e862bc3e8fccadd98c7b0286b /Sluift | |
parent | 41bb9db24566f15d60d2522eaea6f00cbaabdf4a (diff) | |
download | swift-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.cpp | 35 | ||||
-rw-r--r-- | Sluift/ElementConvertors/BodyConvertor.h | 25 | ||||
-rw-r--r-- | Sluift/ElementConvertors/DiscoItemsConvertor.cpp | 18 | ||||
-rwxr-xr-x | Sluift/Examples/Wonderland.lua | 94 | ||||
-rw-r--r-- | Sluift/LuaElementConvertors.cpp | 2 | ||||
-rw-r--r-- | Sluift/SConscript | 1 | ||||
-rw-r--r-- | Sluift/SluiftClient.cpp | 5 | ||||
-rw-r--r-- | Sluift/SluiftClient.h | 4 | ||||
-rw-r--r-- | Sluift/boot.lua | 68 | ||||
-rw-r--r-- | Sluift/client.cpp | 53 |
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; |