diff options
author | Remko Tronçon <git@el-tramo.be> | 2010-10-09 09:52:51 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2010-10-09 14:20:47 (GMT) |
commit | c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4 (patch) | |
tree | ad17bd2ee34263af472340a839a4757859ddaea1 /Documentation | |
parent | 0a84186a22fd48485deed77bc067877ac332d0b4 (diff) | |
download | swift-contrib-c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4.zip swift-contrib-c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4.tar.bz2 |
Added EchoBot walkthrough example.
Diffstat (limited to 'Documentation')
10 files changed, 692 insertions, 14 deletions
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore new file mode 100644 index 0000000..28275ad --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore @@ -0,0 +1,2 @@ +EchoBot? +*.cpp.xml diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp new file mode 100644 index 0000000..584ec9d --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Swiften.h" + +using namespace Swift; + +int main(int, char*) { + SimpleEventLoop eventLoop; + + Client client(JID("echobot@wonderland.lit"), "mypass"); + client.connect(); + + eventLoop.run(); + + return 0; +} diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp new file mode 100644 index 0000000..a03c3db --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <iostream> +#include <boost/bind.hpp> + +#include "Swiften/Swiften.h" + +using namespace Swift; +using namespace boost; + +Client* client; + +void handleConnected() { + std::cout << "Connected" << std::endl; +} + +void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); +} + +int main(int, char*) { + SimpleEventLoop eventLoop; + + client = new Client(JID("echobot@wonderland.lit"), "mypass"); + client->onConnected.connect(&handleConnected); + client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); + client->connect(); + + eventLoop.run(); + + delete client; + return 0; +} diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp new file mode 100644 index 0000000..a9c611f --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <iostream> +#include <boost/bind.hpp> + +#include "Swiften/Swiften.h" + +using namespace Swift; +using namespace boost; + +class EchoBot { + public: + EchoBot() { + client = new Client(JID("echobot@wonderland.lit"), "mypass"); + client->onConnected.connect(bind(&EchoBot::handleConnected, this)); + client->onMessageReceived.connect( + bind(&EchoBot::handleMessageReceived, this, _1)); + tracer = new ClientXMLTracer(client); + client->connect(); + } + + ~EchoBot() { + delete tracer; + delete client; + } + + private: + void handleConnected() { + std::cout << "Connected" << std::endl; + } + + void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); + } + + private: + Client* client; + ClientXMLTracer* tracer; +}; + +int main(int, char*) { + SimpleEventLoop eventLoop; + EchoBot bot; + eventLoop.run(); + return 0; +} diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp new file mode 100644 index 0000000..b01fecd --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +//... +#include <iostream> +#include <boost/bind.hpp> + +#include "Swiften/Swiften.h" + +using namespace Swift; +using namespace boost; +//... +class EchoBot { + public: + EchoBot() { + //... + client = new Client(JID("echobot@wonderland.lit"), "mypass"); + client->onConnected.connect(bind(&EchoBot::handleConnected, this)); + client->onMessageReceived.connect( + bind(&EchoBot::handleMessageReceived, this, _1)); + //... + client->onPresenceReceived.connect( + bind(&EchoBot::handlePresenceReceived, this, _1)); + //... + tracer = new ClientXMLTracer(client); + client->connect(); + //... + } + + //... + ~EchoBot() { + delete tracer; + delete client; + } + + private: + //... + void handlePresenceReceived(Presence::ref presence) { + // Automatically approve subscription requests + if (presence->getType() == Presence::Subscribe) { + Presence::ref response = Presence::create(); + response->setTo(presence->getFrom()); + response->setType(Presence::Subscribed); + client->sendPresence(response); + } + } + + void handleConnected() { + // Request the roster + GetRosterRequest::ref rosterRequest = + GetRosterRequest::create(client->getIQRouter()); + rosterRequest->onResponse.connect( + bind(&EchoBot::handleRosterReceived, this, _2)); + rosterRequest->send(); + } + + void handleRosterReceived(const optional<ErrorPayload>& error) { + if (error) { + std::cerr << "Error receiving roster. Continuing anyway."; + } + // Send initial available presence + client->sendPresence(Presence::create("Send me a message")); + } + //... + + void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); + } + + private: + Client* client; + ClientXMLTracer* tracer; + //... +}; +//... + +int main(int, char*) { + SimpleEventLoop eventLoop; + EchoBot bot; + eventLoop.run(); + return 0; +} +//... diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp new file mode 100644 index 0000000..72d0eb1 --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +//... +#include <iostream> +#include <boost/bind.hpp> + +#include "Swiften/Swiften.h" + +using namespace Swift; +using namespace boost; +//... +class EchoBot { + public: + EchoBot() { + //... + client = new Client(JID("echobot@wonderland.lit"), "mypass"); + client->onConnected.connect(bind(&EchoBot::handleConnected, this)); + client->onMessageReceived.connect( + bind(&EchoBot::handleMessageReceived, this, _1)); + client->onPresenceReceived.connect( + bind(&EchoBot::handlePresenceReceived, this, _1)); + tracer = new ClientXMLTracer(client); + //... + softwareVersionResponder = new SoftwareVersionResponder( + "EchoBot", "1.0", client->getIQRouter()); + //... + client->connect(); + //... + } + + ~EchoBot() { + delete softwareVersionResponder; + //... + delete tracer; + delete client; + //... + } + //... + + private: + void handlePresenceReceived(Presence::ref presence) { + // Automatically approve subscription requests + if (presence->getType() == Presence::Subscribe) { + Presence::ref response = Presence::create(); + response->setTo(presence->getFrom()); + response->setType(Presence::Subscribed); + client->sendPresence(response); + } + } + + void handleConnected() { + // Request the roster + GetRosterRequest::ref rosterRequest = + GetRosterRequest::create(client->getIQRouter()); + rosterRequest->onResponse.connect( + bind(&EchoBot::handleRosterReceived, this, _2)); + rosterRequest->send(); + } + + void handleRosterReceived(const optional<ErrorPayload>& error) { + if (error) { + std::cerr << "Error receiving roster. Continuing anyway."; + } + // Send initial available presence + client->sendPresence(Presence::create("Send me a message")); + } + + void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); + } + + //... + private: + //... + Client* client; + ClientXMLTracer* tracer; + //... + SoftwareVersionResponder* softwareVersionResponder; +}; +//... + +int main(int, char*) { + SimpleEventLoop eventLoop; + EchoBot bot; + eventLoop.run(); + return 0; +} +//... diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript new file mode 100644 index 0000000..ceead6b --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript @@ -0,0 +1,15 @@ +Import("env") + +example_env = env.Clone() +example_env.MergeFlags(example_env["SWIFTEN_FLAGS"]) +example_env.MergeFlags(example_env["LIBIDN_FLAGS"]) +example_env.MergeFlags(example_env["BOOST_FLAGS"]) +example_env.MergeFlags(example_env.get("SQLITE_FLAGS", {})) +example_env.MergeFlags(example_env["ZLIB_FLAGS"]) +example_env.MergeFlags(example_env["OPENSSL_FLAGS"]) +example_env.MergeFlags(example_env.get("LIBXML_FLAGS", "")) +example_env.MergeFlags(example_env.get("EXPAT_FLAGS", "")) +example_env.MergeFlags(example_env["PLATFORM_FLAGS"]) + +for i in range(1,6) : + example_env.Program("EchoBot" + str(i), ["EchoBot" + str(i) + ".cpp"]) diff --git a/Documentation/SwiftenDevelopersGuide/Examples/SConscript b/Documentation/SwiftenDevelopersGuide/Examples/SConscript new file mode 100644 index 0000000..1284b34 --- /dev/null +++ b/Documentation/SwiftenDevelopersGuide/Examples/SConscript @@ -0,0 +1 @@ +SConscript(dirs = ["EchoBot"]) diff --git a/Documentation/SwiftenDevelopersGuide/SConscript b/Documentation/SwiftenDevelopersGuide/SConscript index 145663f..fb08a8f 100644 --- a/Documentation/SwiftenDevelopersGuide/SConscript +++ b/Documentation/SwiftenDevelopersGuide/SConscript @@ -2,4 +2,89 @@ Import("env") env.Tool("DocBook", toolpath = ["#/BuildTools/DocBook/SCons"]) +################################################################################ +# Code generation helper +################################################################################ + +import sys, re, os.path + +def generateDocBookCode(env, target, source) : + # Strips empty lines from the beginning & end of a program + def stripEmptyLines(program) : + programLines = program.split('\n') + newProgramLines = [] + inProgram = False + for line in programLines : + if not re.match("^\s*$", line) or inProgram : + inProgram = True + newProgramLines.append(line) + return '\n'.join(newProgramLines).rstrip() + + def createCallouts(program, calloutPrefix) : + newProgramLines = [] + calloutLines = [] + nextID = 0 + for line in program.split("\n") : + # FIXME: Takes the largest match + m = re.match(".*\/* \(\*\) (.*) \*/.*", line) + if m : + cobID = "cob-" + calloutPrefix + "-" + str(nextID) + coID = "co-" + calloutPrefix + "-" + str(nextID) + nextID += 1 + line = re.sub("/\*.*\*/", "]]><co id=\"%(cobID)s\" linkends=\"%(coID)s\"/><![CDATA[" % {"cobID" : cobID, "coID" : coID}, line) + calloutLines.append("<callout arearefs=\"%(cobID)s\" id=\"%(coID)s\"><para>%(text)s</para></callout>" % {"cobID": cobID, "coID": coID, "text": m.group(1)}) + newProgramLines.append(line) + callouts = "<calloutlist>" + "\n".join(calloutLines) + "</calloutlist>" if len(calloutLines) > 0 else "" + return ("\n".join(newProgramLines), callouts) + + # Parse program + filename = source[0].abspath + filenameBase = os.path.basename(filename).replace(".cpp", "") + inputfile = open(filename) + program = "" + programs = {} + programName = "" + inEllipsis = False + for line in inputfile.readlines() : + if inEllipsis : + if "//..." in line : + inEllipsis = False + else : + if line.startswith("/*") or line.startswith(" *") : + continue + if "//..." in line : + inEllipsis = True + line = line.replace("//...", "]]>…<![CDATA[") + else : + m = re.match("^/// (.*)", line) + if m : + if programName : + programs[programName] = program + program = "" + programName = m.group(1).strip().replace(" ", "") + continue + line = re.sub("\t", " ", line) + program += line + programs[programName] = program + inputfile.close() + + for programName, program in programs.items() : + program = stripEmptyLines(program) + (program, callouts) = createCallouts(program, filenameBase + "-" + programName) + + document = "<foo><programlisting><![CDATA[" + program + "]]></programlisting>" + callouts + "</foo>" + + # Generate code + output = open(target[0].abspath, 'w') + output.write(document) + output.close() + +################################################################################ + env.DocBook("Swiften Developers Guide.xml") + +for i in range(1, 6) : + source = "Examples/EchoBot/EchoBot" + str(i) + ".cpp" + env.Command(source + ".xml", source, Action(generateDocBookCode, cmdstr = "$GENCOMSTR")) + +SConscript(dirs = ["Examples"]) diff --git a/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml b/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml index e0a63ae..1df3781 100644 --- a/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml +++ b/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml @@ -6,44 +6,322 @@ <chapter> <title>Introduction</title> + <section> + <title>Prerequisites</title> + <para> + We assume that the reader is familiar with the basics of the + XMPP protocol. For an overview of the XMPP protocol and its + workings, see + <citetitle>XMPP: The Definitive Guide</citetitle> + <citation><biblioref linkend="XMPP-TDG"/></citation> + </para> + </section> - <para> - This is an introduction - </para> - - <sect1> - <title>Section</title> - + <section> + <title>Boost</title> <para> - This is a section + Swiften makes heavy use of <emphasis>Boost</emphasis> (<ulink url="http://boost.org"><uri>http://boost.org</uri></ulink>) libraries, including <emphasis>Signal</emphasis>, + <emphasis>Bind</emphasis>, <emphasis>Optional</emphasis>, and + <emphasis>Smart Pointers</emphasis>. We + introduce the basic usage of these libraries in our API throughout + this manual. For detailed documentation, we refer to the Boost + website. </para> - </sect1> + </section> </chapter> <chapter> <title>Tutorial: Writing an Echo Bot</title> <para> - In this chapter, we build a simple echo bot, illustrating how - to use and extend Swiften for your own purposes. + In this chapter, we guide you through the Swiften API by building an + example XMPP application: an EchoBot. This example program, taken from + <citetitle>XMPP: The Definitive Guide</citetitle> + <citation><biblioref linkend="XMPP-TDG"/></citation>, connects to + an XMPP server, logs in, and responds to all incoming messages with + the exact same message. We build up our application using Swiften's + basic building blocks for XMPP development, to help get a good + understanding of how Swiften fundamental classes work and can be + extended. In the last stage of this example, we + introduce some of Swiften's convenience classes for standard + XMPP tasks such as roster management. </para> <sect1> - <title>Setting up the build environment</title> + <title>Connecting to a server: Clients & event loops</title> + <para> + As a first step, we create an application that connects to a server. + The code can be seen in <xref linkend="Example-EchoBot1"/>. + </para> + <example id="Example-EchoBot1"> + <title>Connecting to a server</title> + <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot1.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/> + </example> + <para> + The first thing this program does is construct an + <emphasis>Event Loop</emphasis>. An event loop is a seemingly infinite + loop that waits for external events (e.g. incoming network packets, + timers being activated, input happening) to happen; when such an event + comes in, it notifies interested parties of this event, and then + continues listening for the next event. Since many application + frameworks (such as Qt, GLib, Cocoa) use their own event loop, + Swiften comes prepackaged with classes that integrate with these + event loops. These classes can be found in + <literal>Swiften/EventLoop</literal>. + In this example, however, we don't use such a framework, + so we use Swiften's own <literal>SimpleEventLoop</literal>. This + class is used by simply instantiating it at the beginning of the + application, and calling <literal>run()</literal> after everything + is set up, which will go into an infinite loop. Apart from constructing + and (if necessary) starting the event loop, you will probably have + no other contact with it in the rest of the application. + </para> + + <para> + Swiften's central class for implementing XMPP applications is + <literal>Client</literal>. This class handles all the interaction + with the XMPP network. After constructing it with the JID and + password with which we want to connect, we call + <literal>connect()</literal> to instruct the client to connect to + the XMPP server with the given credentials. Note that this call returns + immediately; it is only when starting the event loop that network + the actual connection process will start. + </para> + </sect1> + + <sect1> + <title>Building EchoBot</title> + <remark>TODO: Explain linking against the static library.</remark> + </sect1> + + <sect1> + <title>Reacting to events: Signals, Slots & Bind</title> + <para> + Up to this point, our client doesn't do anything useful. In this + section, we make the client react to XMPP events. The code can + be seen in <xref linkend="Example-EchoBot2"/>. + </para> + <example id="Example-EchoBot2"> + <title>Reacting to events: Notify whenever the client is connected to the network, and echo back incoming messages</title> + <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot2.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/> + </example> + + <para> + A first thing we want to do is print out a message when the client + is connected to the server. Swiften uses the + <emphasis>signal/slot</emphasis> paradigm for notifying interested + parties of events. A <emphasis>signal</emphasis> is an object + representing a type of event. For example, <literal>Client</literal> + has an <literal>onConnected</literal> signal for notifying whenever + the client is connected to the network. If you are interested in + a particular signal, you connect a <emphasis>slot</emphasis> to the + signal. A slot represents a callback that will be called whenever a + signal is emitted. Since we want to print out a message whenever + we're connected to the network, we connect to the client's signal, + and tell it to call <literal>handleConnected</literal> (which prints + out a message): + <programlisting>client->onConnected.connect(&handleConnected)</programlisting> + </para> + + <para> + Another event we're interested in is whenever a message comes in. + For this purpose, <literal>Client</literal> provides a signal called + <literal>onMessageReceived</literal>. The major difference with the + previous <literal>onConnected</literal> signal is that this signal + also can provide extra information to the callback: the actual + message received. A signal can provide this extra information through + one or more arguments, which will be passed to the slot's parameters. + To be able to handle parameters to slots, there needs to be a more + general representation of callbacks than just function pointers. This + is where Boost's <literal>bind</literal> comes in: <literal>bind</literal> + provides a way to construct <emphasis>functors</emphasis> (callbacks, slots, …), by combining function pointers and parameter values. + For example, to connect the signal to our slot, we call: + <programlisting>client->onMessageReceived.connect(bind(&handleMessageReceived, _1))</programlisting> + This is essentially saying: when the + <literal>onMessageReceived</literal> signal is emitted, call + <literal>handleMessageReceived</literal>, and pass it the first + parameter provided by the slot (which, in this case, is the actual + message received). + </para> + <para> + The implementation of <literal>handleMessageReceived</literal> should be straightforward: put the <emphasis>To</emphasis> address in place of the <emphasis>From</emphasis> address, and send the message to the server. One + thing to note is that <literal>Message::ref</literal> represents a + <emphasis>shared pointer</emphasis> to a <literal>Message</literal> + stanza. Shared pointers behave the same as regular pointers, except that, + when the last copy of the pointer goes away, the object it points to is + deleted as well. <literal>Message::ref</literal> is in fact a + typedef for <literal>boost::shared_ptr<Message></literal>. + Although Swiften tends to prefer the use of the <literal>::ref</literal> + notation, you will see both forms used intermixed. + </para> + + <para> + Before moving on to the next step, we are going to rearrange our + code a bit, to make it a bit cleaner. Instead of using global + variables, we are going to create an <literal>EchoBot</literal> + class with the current code in it. The resulting code can be found + in <xref linkend="Example-EchoBot3"/>. + </para> + <example id="Example-EchoBot3"> + <title>Creating an EchoBot class</title> + <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot3.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/> + </example> + <para> + The important thing to consider in this step are the changes to the signal + connections. Since we are now passing member variables of a class + to the signal, we need to use <literal>bind</literal> to pass + in the actual object on which this member variable is called as + the first parameter. + </para> + <para> + The only thing we added to this version is the + <literal>ClientXMLTracer</literal>. This class will dump all + incoming and outgoing XMPP messages to the console, which can be handy + for debugging our bot. </para> </sect1> <sect1> - <title>Connecting to the network</title> + <title>Presence Management: Requests</title> <para> + The current version of our EchoBot does what it is supposed to do: + it answers all incoming messages. However, although users can add + the bot to their contact list, they will not see when it is + online, since the bot doesn't do any presence handling yet. In + this section, we explain the different steps involved in adding + presence management, resulting in the code in + <xref linkend="Example-EchoBot4"/>. + </para> + <example id="Example-EchoBot4"> + <title>Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests.</title> + <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot4.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/> + </example> + <para> + First of all, our bot needs to listen to incoming subscription requests + from users who want to add it to their roster, and automatically + approve them. This is done by connecting to + the <literal>onPresenceReceived</literal> signal, checking whether + the incoming presence is a subscription request, and if so, + respond to it with an approval (in <literal>handlePresenceReceived</literal>). + </para> + + <para> + The first version of the XMPP protocol states that a client will not + get any presence + subscriptions until it requests the roster. To make sure we handle + this, we want to make sure our bot requests the roster at login. + After getting the <literal>onConnected</literal> signal, we + therefore send a + <emphasis>request</emphasis> to retrieve the roster. Swiften's + <literal>Request</literal> classes correspond to XMPP IQ Get or + Set actions. Swiften provides a set of built-in request classes for + the most common tasks in <literal>Swiften/Queries/Requests</literal>, + and can be easily extended to use add your own (see <xref linkend="Section-Extending"/>). Requests have an <literal>onResponse</literal> signal, + which is emitted when a response comes in. This signal has 2 parameters: + the actual response data (the <emphasis>Payload</emphasis>), and an + optional error payload in case there was an error executing the + request. To use a <literal>Request</literal> class, you construct + it with the correct parameters, connect to the + <literal>onResponse</literal> signal, and then send the request by + calling <literal>send()</literal> on it. In this case, we're not + interested in the actual payload of the response (passed as the + first parameter), so we pass it a slot with only the second parameter + (the error payload). When we get the roster back, we send initial + presence to all our subscribers, announcing them we're online. </para> </sect1> <sect1> - <title>T</title> + <title>Publishing version information: Responders</title> <para> + Most XMPP clients have support for querying software version information + of a client through + <citation><biblioref linkend="XEP-0092"/></citation>. These clients + send out an IQ-Get request to an entity, which responds with the + requested information. We would like our bot to listen to these + requests, and respond with the correct information. Swiften uses + <literal>Responder</literal> classes for the purpose of responding + to IQ requests, and are therefore the dual of the + <literal>Request</literal> clients discussed in the previous section. </para> + + <example id="Example-EchoBot5"> + <title>Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests.</title> + <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot5.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/> + </example> + + <para> + Using <literal>SoftwareVersionResponder</literal> is pretty + straightforward, as can be seen in <xref linkend="Example-EchoBot5"/>: + simply construct the responder with the correct + parameters, and it will automatically respond to the incoming + requests. Other <literal>Responder</literal> classes may provide + signals to notify of incoming requests, or may have some other + behavior. For a detailed explanation of responders, see + <xref linkend="Section-CustomQueries"/>. + </para> + </sect1> + + <sect1 id="Section-Extending"> + <title>Extending Swiften with new payloads: Payloads, Parsers, and Serializers</title> + <para> + Swiften uses abstract datastructures for all the data that is received + and sent over the XMPP network. The declaration of these datastructures + can all be found in <literal>Swiften/Elements</literal>. For + representing the XMPP stanzas, Swiften uses the + <literal>Message</literal>, <literal>Presence</literal>, and + <literal>IQ</literal> classes. Each stanza can have an arbitrary + amount of child <emphasis>payloads</emphasis>, represented by the + <literal>Payload</literal> class. A payload typically corresponds + to a (namespaced) child XML element of a stanza; for example, the + <literal><query xmlns="jabber:iq:roster"/></literal> + element used for managing the roster is represented as a + <literal>RosterPayload</literal>. + </para> + + <remark>TODO</remark> + </sect1> + + <sect1 id="Section-CustomQueries"> + <title>Extending Swiften with new queries and responders</title> + <remark>TODO</remark> + </sect1> + + <sect1> + <title>Using Swiften's convenience classes</title> + <remark>TODO</remark> </sect1> </chapter> + + <bibliography> + <title>Bibliography</title> + + <biblioentry id="XMPP-TDG"> + <abbrev>XMPP-TDG</abbrev> + <title><ulink url="http://oreilly.com/catalog/9780596157197/">XMPP: The + Definitive Guide</ulink></title> + <author> + <firstname>Peter</firstname> + <surname>Saint-Andre</surname> + </author> + <author> + <firstname>Kevin</firstname> + <surname>Smith</surname> + </author> + <author> + <firstname>Remko</firstname> + <surname>Tronçon</surname> + </author> + </biblioentry> + + <biblioentry id='XEP-0092'> + <abbrev>XEP-0092</abbrev> + <title><ulink url='http://www.xmpp.org/extensions/xep-0092.html'>Software Version</ulink></title> + <author> + <firstname>Peter</firstname> + <surname>Saint-Andre</surname> + </author> + </biblioentry> + </bibliography> </book> |