JabChapter 9

From WikiContent

(Difference between revisions)
Jump to: navigation, search
Line 1: Line 1:
 +
[http://aquisechatea.com/galeria/albums/userpics/10002/page115.html silent movie theater on fairfax] [http://woningpagina.com/pics/flash/comment-1097.html american express money wire] [http://appleturnover.tv/about/images/news-638-20090704.html las vegas poker tournaments february] [http://carpentercarpenter.ca/_doubletuttle/images/comment-1231.html printed sweatshirts] [http://aarh.net/generator/assets/gallery3/topic-61.html gazebo designs] [http://techwebcast.info/forum/attachments/files/page-347.html wwe diva stacy keibler.] [http://ackcsc.org/club.bak/media/flash/bascfu.htm razzle] [http://aarh.net/generator/assets/gallery3/index.html map] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-17.htm iowa city entertainment] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-2047.htm walley fishing manitoba] [http://tatlin.com/gallery/wp-content/uploads/2008/index.htm site] [http://casv.ca/secondary/class/install/1376.html indecent proposal video clip] [http://techwebcast.info/forum/attachments/files/page-1205.html free gd55 mobile panasonic ringtone] [http://beachcki.org/officers/wpThumbnails/pics/topic519.htm moms movies.com] [http://rvpark4u.com/weblog/skins/rsd/files/page181.html a perfect circle music video downloads] [http://carpentercarpenter.ca/_doubletuttle/images/comment-1169.html amateur submitted movies] [http://aarh.net/generator/assets/gallery3/topic-925.html auto nationwide watch.com] [http://techwebcast.info/forum/attachments/files/page-395.html marisa miller video] [http://aarh.net/generator/assets/gallery3/topic-1595.html free mobile panasonic ringtone] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-1203.htm movie schedule tinseltown] [http://rubyarts.org/images/icons/news-486.html sponges phylum] [http://juliemorstad.com/contactform/forms/images/article1130.htm popup blockers] [http://brixhost.net/my/attachments/media/news-228-2009-07-02.html russian mature movies] [http://pixsoulmedia.com/dev/images/thumbs/article422.html secretely] [http://casv.ca/secondary/class/install/2418.html music roger video zapp] [http://cavaliersonline.com/photos/icons/text1385.htm nude madonna] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-53.htm dan post boots] [http://beachcki.org/officers/wpThumbnails/pics/sitemap.html sitemap] [http://appleturnover.tv/about/images/news-2163-20090630.html annie lennox ringtone] [http://beachcki.org/officers/wpThumbnails/pics/topic777.htm framed photos] [http://pixsoulmedia.com/dev/images/thumbs/article2357.html movie clip heather graham] [http://ackcsc.org/club.bak/media/flash/index.htm link] [http://aquisechatea.com/galeria/albums/userpics/10002/page214.html mass transit magazines] [http://castlekeepcavaliers.com/generator/assets/files/page9.html coach replica] [http://appleturnover.tv/about/images/news-1467-20090629.html amr ringtones free download] [http://appleturnover.tv/about/images/news-955-20090622.html english to german] [http://thewholesaler.co.uk/newsletter/data/images/article1692.html masterbate clit] [http://aquisechatea.com/galeria/albums/userpics/10002/page1211.html electric burr grinder] [http://pixsoulmedia.com/dev/images/thumbs/article1923.html pharmacy] [http://pixsoulmedia.com/dev/images/thumbs/article946.html ethnic video] [http://rubyarts.org/images/icons/news-1767.html sexual intercourse photos] [http://cavalierpedigrees.com/XOOPS/images/text1216.htm deer hunting] [http://rvpark4u.com/weblog/skins/rsd/files/page21.html igrice video] [http://cavaliersonline.com/photos/icons/text1365.htm microsoft open license site] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-2253.htm nostalgiques stickers] [http://rvpark4u.com/weblog/skins/rsd/files/page1677.html russian movie download] [http://thewholesaler.co.uk/newsletter/data/images/article792.html movie icon avatars] [http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-1179.html anna ohura videos] [http://rubyarts.org/images/icons/news-60.html phentermine pharmacy biz] [http://cavalierpedigrees.com/XOOPS/images/text1532.htm wet nurses]
[http://brixhost.net/my/attachments/media/news-1356-2009-06-25.html white water rafting wales uk] [http://ackcsc.org/club.bak/media/flash/ricere.htm playstation3] [http://techwebcast.info/forum/attachments/files/page-2212.html merman movies] [http://arccc-cccaa.org/images/thumbs/topic-1515.htm ritalin adderall concerta] [http://cavalierpedigrees.com/XOOPS/images/sitemap.htm sitemap] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-586.htm decadron] [http://aquisechatea.com/galeria/albums/userpics/10002/page111.html preview torrent file] [http://castlekeepcavaliers.com/generator/assets/files/page1490.html mpeg4 movie converter] [http://appleturnover.tv/about/images/news-2037-20090624.html asia movie trailer] [http://rubyarts.org/images/icons/news-730.html craigs list denver] [http://mdabc.net/test/files/new1844.htm irish drink mix] [http://vfcpug.org/meta/quad/images/text-772.htm 2005 draft nhl] [http://techwebcast.info/forum/attachments/files/page-1897.html media player movie trailors] [http://casv.ca/secondary/class/install/500.html leonard maltin movie reviews] [http://marja-leena-rathje.info/ssp_director/config/conf/article2112.html apache mysql php windows installer] [http://aarh.net/generator/assets/gallery3/topic-1025.html hohner] [http://vfcpug.org/meta/quad/images/text-1058.htm nik sharpener pro serial] [http://arccc-cccaa.org/images/thumbs/sitemap.htm index] [http://brixhost.net/my/attachments/media/index.html link] [http://carpentercarpenter.ca/_doubletuttle/images/comment-600.html penneys] [http://rubyarts.org/images/icons/news-41.html radiator hose] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-2382.htm wu-tang clan] [http://paarc.ca/images/thumbs/article2179.html woman naked] [http://ackcsc.org/club.bak/media/flash/devarnoe.htm lowden guitar serial numbers] [http://techwebcast.info/forum/attachments/files/page-1136.html recurso humanos] [http://aquisechatea.com/galeria/albums/userpics/10002/page1224.html matthew perry drug addiction] [http://techwebcast.info/forum/attachments/files/page-1664.html kill myself] [http://mdabc.net/test/files/new771.htm eminem puke video] [http://atomized.ca/2006/12/news-2746-20090622.html easy producer video] [http://vfcpug.org/meta/quad/images/text-236.htm aspiration book guest] [http://castlekeepcavaliers.com/generator/assets/files/page370.html lex on blondes] [http://mdabc.net/test/files/sitemap.htm domain] [http://paarc.ca/images/thumbs/article1362.html oxycontin lil wyte lyrics] [http://appleturnover.tv/about/images/news-1073-20090628.html what is extacy drug] [http://mdabc.net/test/files/new422.htm pretty ricky video] [http://pixsoulmedia.com/dev/images/thumbs/article966.html total wine] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-194.htm naked photo post] [http://pixsoulmedia.com/dev/images/thumbs/article2578.html adam brody jimmy kimmel] [http://arccc-cccaa.org/images/thumbs/topic-847.htm best way to lose weight] [http://appleturnover.tv/about/images/news-1792-20090705.html magnum movie p.i] [http://thewholesaler.co.uk/newsletter/data/images/article1266.html infant blood pressure normal] [http://woningpagina.com/pics/flash/comment-23.html plastic license plate blanks] [http://cavaliersonline.com/photos/icons/text1285.htm allfreegay] [http://brixhost.net/my/attachments/media/news-770-2009-06-23.html trackpad powerbook] [http://techwebcast.info/forum/attachments/files/page-187.html adobe photoshop 8 cs cracked] [http://arccc-cccaa.org/images/thumbs/topic-1083.htm interpreter] [http://woningpagina.com/pics/flash/comment-445.html memphis homes] [http://castlekeepcavaliers.com/generator/assets/files/page1309.html el nino] [http://castlekeepcavaliers.com/generator/assets/files/page1399.html red envolope] [http://techwebcast.info/forum/attachments/files/page-964.html irene hunt pictures]
[http://brixhost.net/my/attachments/media/news-1356-2009-06-25.html white water rafting wales uk] [http://ackcsc.org/club.bak/media/flash/ricere.htm playstation3] [http://techwebcast.info/forum/attachments/files/page-2212.html merman movies] [http://arccc-cccaa.org/images/thumbs/topic-1515.htm ritalin adderall concerta] [http://cavalierpedigrees.com/XOOPS/images/sitemap.htm sitemap] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-586.htm decadron] [http://aquisechatea.com/galeria/albums/userpics/10002/page111.html preview torrent file] [http://castlekeepcavaliers.com/generator/assets/files/page1490.html mpeg4 movie converter] [http://appleturnover.tv/about/images/news-2037-20090624.html asia movie trailer] [http://rubyarts.org/images/icons/news-730.html craigs list denver] [http://mdabc.net/test/files/new1844.htm irish drink mix] [http://vfcpug.org/meta/quad/images/text-772.htm 2005 draft nhl] [http://techwebcast.info/forum/attachments/files/page-1897.html media player movie trailors] [http://casv.ca/secondary/class/install/500.html leonard maltin movie reviews] [http://marja-leena-rathje.info/ssp_director/config/conf/article2112.html apache mysql php windows installer] [http://aarh.net/generator/assets/gallery3/topic-1025.html hohner] [http://vfcpug.org/meta/quad/images/text-1058.htm nik sharpener pro serial] [http://arccc-cccaa.org/images/thumbs/sitemap.htm index] [http://brixhost.net/my/attachments/media/index.html link] [http://carpentercarpenter.ca/_doubletuttle/images/comment-600.html penneys] [http://rubyarts.org/images/icons/news-41.html radiator hose] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-2382.htm wu-tang clan] [http://paarc.ca/images/thumbs/article2179.html woman naked] [http://ackcsc.org/club.bak/media/flash/devarnoe.htm lowden guitar serial numbers] [http://techwebcast.info/forum/attachments/files/page-1136.html recurso humanos] [http://aquisechatea.com/galeria/albums/userpics/10002/page1224.html matthew perry drug addiction] [http://techwebcast.info/forum/attachments/files/page-1664.html kill myself] [http://mdabc.net/test/files/new771.htm eminem puke video] [http://atomized.ca/2006/12/news-2746-20090622.html easy producer video] [http://vfcpug.org/meta/quad/images/text-236.htm aspiration book guest] [http://castlekeepcavaliers.com/generator/assets/files/page370.html lex on blondes] [http://mdabc.net/test/files/sitemap.htm domain] [http://paarc.ca/images/thumbs/article1362.html oxycontin lil wyte lyrics] [http://appleturnover.tv/about/images/news-1073-20090628.html what is extacy drug] [http://mdabc.net/test/files/new422.htm pretty ricky video] [http://pixsoulmedia.com/dev/images/thumbs/article966.html total wine] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-194.htm naked photo post] [http://pixsoulmedia.com/dev/images/thumbs/article2578.html adam brody jimmy kimmel] [http://arccc-cccaa.org/images/thumbs/topic-847.htm best way to lose weight] [http://appleturnover.tv/about/images/news-1792-20090705.html magnum movie p.i] [http://thewholesaler.co.uk/newsletter/data/images/article1266.html infant blood pressure normal] [http://woningpagina.com/pics/flash/comment-23.html plastic license plate blanks] [http://cavaliersonline.com/photos/icons/text1285.htm allfreegay] [http://brixhost.net/my/attachments/media/news-770-2009-06-23.html trackpad powerbook] [http://techwebcast.info/forum/attachments/files/page-187.html adobe photoshop 8 cs cracked] [http://arccc-cccaa.org/images/thumbs/topic-1083.htm interpreter] [http://woningpagina.com/pics/flash/comment-445.html memphis homes] [http://castlekeepcavaliers.com/generator/assets/files/page1309.html el nino] [http://castlekeepcavaliers.com/generator/assets/files/page1399.html red envolope] [http://techwebcast.info/forum/attachments/files/page-964.html irene hunt pictures]
[http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-769.html pussy eating gallery] [http://marja-leena-rathje.info/ssp_director/config/conf/article1575.html free web design template] [http://appleturnover.tv/about/images/news-149-20090627.html pwl cracker download] [http://rubyarts.org/images/icons/news-1093.html activation end process product software] [http://arccc-cccaa.org/images/thumbs/topic-426.htm cell motorola phone razr ringtone] [http://thewholesaler.co.uk/newsletter/data/images/article376.html showplace 16 coon rapids mn] [http://rubyarts.org/images/icons/index.html url] [http://ackcsc.org/club.bak/media/flash/bugpaslet.htm in nevada pharmacy school] [http://rvpark4u.com/weblog/skins/rsd/files/page2269.html sexy seamed stockings] [http://paarc.ca/images/thumbs/article1087.html sample sex video and movie] [http://ackcsc.org/club.bak/media/flash/nrdron.htm speculumpages] [http://brixhost.net/my/attachments/media/news-361-2009-06-24.html archive video files] [http://casv.ca/secondary/class/install/1859.html free ringtones alltel audiovox] [http://appleturnover.tv/about/images/news-393-20090703.html nastyrat.com] [http://cavaliersonline.com/photos/icons/text1417.htm marmalade mr review] [http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-942.html lolita index] [http://cavalierpedigrees.com/XOOPS/images/text1805.htm alive blaine clip david drowned video] [http://woningpagina.com/pics/flash/comment-236.html pioneer press] [http://arccc-cccaa.org/images/thumbs/sitemap.htm page] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-446.htm aurora fox mall valley] [http://aarh.net/generator/assets/gallery3/topic-1709.html 2 best camera digital megapixel] [http://ackcsc.org/club.bak/media/flash/sitemap.htm links] [http://brixhost.net/my/attachments/media/news-314-2009-06-27.html eating a balanced diet] [http://cavaliersonline.com/photos/icons/text1196.htm adultactioncam.com] [http://paarc.ca/images/thumbs/article2649.html cardizem cd anabolic steroids actos altace] [http://beachcki.org/officers/wpThumbnails/pics/topic1242.htm robert palmer simply irresistible music video] [http://cavalierpedigrees.com/XOOPS/images/text395.htm girls messy cheerleader] [http://arccc-cccaa.org/images/thumbs/topic-1260.htm pacific assault no cd crack] [http://techwebcast.info/forum/attachments/files/page-1864.html shemale domination] [http://beachcki.org/officers/wpThumbnails/pics/topic228.htm lunchboxes] [http://beachcki.org/officers/wpThumbnails/pics/topic1028.htm live night rap saturday video] [http://rvpark4u.com/weblog/skins/rsd/files/page1867.html karate kata video free] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-1435.htm ohiolottery] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-869.htm rakuen movie 4 inuyasha] [http://carpentercarpenter.ca/_doubletuttle/images/comment-1352.html smoothmag] [http://kazak.ca/buzz/data/media/boclid1366.htm wholesale home tanning bed] [http://juliemorstad.com/contactform/forms/images/article1485.htm honda radiator] [http://sahardesign.com/files/styles/article89.html skydiving video clip] [http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-1164.html movie lobby card] [http://juliemorstad.com/contactform/forms/images/index.htm index] [http://beachcki.org/officers/wpThumbnails/pics/topic1352.htm environmental sampling supplies] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-1226.htm valassis] [http://marja-leena-rathje.info/ssp_director/config/conf/article2186.html ivp care pharmacy] [http://appleturnover.tv/about/images/news-341-20090630.html pure country movie quotes] [http://cavalierpedigrees.com/XOOPS/images/text335.htm student housing] [http://casv.ca/secondary/class/install/1589.html chicago cubs] [http://sahardesign.com/files/styles/sitemap.html index] [http://kazak.ca/buzz/data/media/acerko633.htm pictures of rainbows] [http://sahardesign.com/files/styles/sitemap.html top] [http://cavalierpedigrees.com/XOOPS/images/text1627.htm suffolk realty agents]
[http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-769.html pussy eating gallery] [http://marja-leena-rathje.info/ssp_director/config/conf/article1575.html free web design template] [http://appleturnover.tv/about/images/news-149-20090627.html pwl cracker download] [http://rubyarts.org/images/icons/news-1093.html activation end process product software] [http://arccc-cccaa.org/images/thumbs/topic-426.htm cell motorola phone razr ringtone] [http://thewholesaler.co.uk/newsletter/data/images/article376.html showplace 16 coon rapids mn] [http://rubyarts.org/images/icons/index.html url] [http://ackcsc.org/club.bak/media/flash/bugpaslet.htm in nevada pharmacy school] [http://rvpark4u.com/weblog/skins/rsd/files/page2269.html sexy seamed stockings] [http://paarc.ca/images/thumbs/article1087.html sample sex video and movie] [http://ackcsc.org/club.bak/media/flash/nrdron.htm speculumpages] [http://brixhost.net/my/attachments/media/news-361-2009-06-24.html archive video files] [http://casv.ca/secondary/class/install/1859.html free ringtones alltel audiovox] [http://appleturnover.tv/about/images/news-393-20090703.html nastyrat.com] [http://cavaliersonline.com/photos/icons/text1417.htm marmalade mr review] [http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-942.html lolita index] [http://cavalierpedigrees.com/XOOPS/images/text1805.htm alive blaine clip david drowned video] [http://woningpagina.com/pics/flash/comment-236.html pioneer press] [http://arccc-cccaa.org/images/thumbs/sitemap.htm page] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-446.htm aurora fox mall valley] [http://aarh.net/generator/assets/gallery3/topic-1709.html 2 best camera digital megapixel] [http://ackcsc.org/club.bak/media/flash/sitemap.htm links] [http://brixhost.net/my/attachments/media/news-314-2009-06-27.html eating a balanced diet] [http://cavaliersonline.com/photos/icons/text1196.htm adultactioncam.com] [http://paarc.ca/images/thumbs/article2649.html cardizem cd anabolic steroids actos altace] [http://beachcki.org/officers/wpThumbnails/pics/topic1242.htm robert palmer simply irresistible music video] [http://cavalierpedigrees.com/XOOPS/images/text395.htm girls messy cheerleader] [http://arccc-cccaa.org/images/thumbs/topic-1260.htm pacific assault no cd crack] [http://techwebcast.info/forum/attachments/files/page-1864.html shemale domination] [http://beachcki.org/officers/wpThumbnails/pics/topic228.htm lunchboxes] [http://beachcki.org/officers/wpThumbnails/pics/topic1028.htm live night rap saturday video] [http://rvpark4u.com/weblog/skins/rsd/files/page1867.html karate kata video free] [http://caypkiwanis.org/main/wp-content/uploads/2007/09/article-1435.htm ohiolottery] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-869.htm rakuen movie 4 inuyasha] [http://carpentercarpenter.ca/_doubletuttle/images/comment-1352.html smoothmag] [http://kazak.ca/buzz/data/media/boclid1366.htm wholesale home tanning bed] [http://juliemorstad.com/contactform/forms/images/article1485.htm honda radiator] [http://sahardesign.com/files/styles/article89.html skydiving video clip] [http://capstonerealty.ph/gallery/Farm_Lots/mini/resource-1164.html movie lobby card] [http://juliemorstad.com/contactform/forms/images/index.htm index] [http://beachcki.org/officers/wpThumbnails/pics/topic1352.htm environmental sampling supplies] [http://tatlin.com/gallery/wp-content/uploads/2008/topic-1226.htm valassis] [http://marja-leena-rathje.info/ssp_director/config/conf/article2186.html ivp care pharmacy] [http://appleturnover.tv/about/images/news-341-20090630.html pure country movie quotes] [http://cavalierpedigrees.com/XOOPS/images/text335.htm student housing] [http://casv.ca/secondary/class/install/1589.html chicago cubs] [http://sahardesign.com/files/styles/sitemap.html index] [http://kazak.ca/buzz/data/media/acerko633.htm pictures of rainbows] [http://sahardesign.com/files/styles/sitemap.html top] [http://cavalierpedigrees.com/XOOPS/images/text1627.htm suffolk realty agents]
Line 11: Line 12:
conference room and alerts us to words and phrases that we want it to
conference room and alerts us to words and phrases that we want it to
listen for. There are two popular conference protocols, as mentioned in
listen for. There are two popular conference protocols, as mentioned in
-
Section 6.2.6—the presence-based Groupchat protocol, and the
+
Section 6.2.6—the presence-based Groupchat protocol, and the
<tt>jabber:iq:conference</tt>-based Conference protocol. The assistant
<tt>jabber:iq:conference</tt>-based Conference protocol. The assistant
recipe, a foray into the world of 'bots, takes a look at the original
recipe, a foray into the world of 'bots, takes a look at the original
Line 21: Line 22:
where this melding needs to happen. The first is a homage to the Trojan
where this melding needs to happen. The first is a homage to the Trojan
Room Coffee Machine (http://www.cl.cam.ac.uk/coffee/coffee.html), where
Room Coffee Machine (http://www.cl.cam.ac.uk/coffee/coffee.html), where
-
we give life, or at least presence, to a coffeepot, using LEGO®
+
we give life, or at least presence, to a coffeepot, using LEGO®
-
MINDSTORMS™. The second is a Tk-based RSS headline viewer. Both the
+
MINDSTORMS™. The second is a Tk-based RSS headline viewer. Both the
coffeepot and the Tk programming library have event loops of their own.
coffeepot and the Tk programming library have event loops of their own.
With the coffeepot, we need to have a loop that polls the coffeepot's
With the coffeepot, we need to have a loop that polls the coffeepot's
Line 46: Line 47:
between programming a client and programming a component in this chapter
between programming a client and programming a component in this chapter
and build a complete component that can be queried and interacted with
and build a complete component that can be queried and interacted with
-
using the third of Jabber's building blocks—the <tt>&lt;iq/&gt;</tt>
+
using the third of Jabber's building blocks—the <tt>&lt;iq/&gt;</tt>
element.
element.
Line 89: Line 90:
becoming a conference user identified by a nickname that is chosen upon
becoming a conference user identified by a nickname that is chosen upon
entering that room. Nicknames are generally used in conference rooms to
entering that room. Nicknames are generally used in conference rooms to
-
provide a modicum of privacy—it is assumed that by default you don't
+
provide a modicum of privacy—it is assumed that by default you don't
want to let the other conference room members know your real JID.
want to let the other conference room members know your real JID.
Line 95: Line 96:
interaction: a simple one that provides basic features and a more
interaction: a simple one that provides basic features and a more
complex one that provides the basic features plus facilities such as
complex one that provides the basic features plus facilities such as
-
password-protected rooms and room descriptions—Groupchat and Conference.
+
password-protected rooms and room descriptions—Groupchat and Conference.
Line 245: Line 246:
: <tt>error</tt> and comes from the artificial JID constructed in the
: <tt>error</tt> and comes from the artificial JID constructed in the
: room entry attempt. The element is addressed to <tt>qmacro</tt>'s real
: room entry attempt. The element is addressed to <tt>qmacro</tt>'s real
-
: JID, of course—<tt>qmacro@jabber.com/jarltk</tt>—as otherwise it
+
: JID, of course—<tt>qmacro@jabber.com/jarltk</tt>—as otherwise it
: wouldn't reach him. : The error code 409 and text "Conflict" tells
: wouldn't reach him. : The error code 409 and text "Conflict" tells
: <tt>qmacro</tt> that the nickname conflicted with one already in the
: <tt>qmacro</tt> that the nickname conflicted with one already in the
Line 259: Line 260:
</code>
</code>
-
: This time, there is no conflict—no other user is in the room "cellar"
+
: This time, there is no conflict—no other user is in the room "cellar"
-
: with that nickname—and the conference component registers the entry.
+
: with that nickname—and the conference component registers the entry.
: It does this by sending <tt>qmacro</tt> the presence of all the room
: It does this by sending <tt>qmacro</tt> the presence of all the room
: occupants, including that of himself:
: occupants, including that of himself:
Line 351: Line 352:
: never discovers the real JID of the sender. In all other ways, the
: never discovers the real JID of the sender. In all other ways, the
: actual <tt>&lt;message/&gt;</tt> element is like any other
: actual <tt>&lt;message/&gt;</tt> element is like any other
-
: <tt>&lt;message/&gt;</tt> element—in this case, it contains a message
+
: <tt>&lt;message/&gt;</tt> element—in this case, it contains a message
: <tt>&lt;body/&gt;</tt> and a chat <tt>&lt;thread/&gt;</tt>. (See
: <tt>&lt;body/&gt;</tt> and a chat <tt>&lt;thread/&gt;</tt>. (See
: Section 5.4.1 for details on the <tt>&lt;message/&gt;</tt> element.)
: Section 5.4.1 for details on the <tt>&lt;message/&gt;</tt> element.)
Line 414: Line 415:
We can see from the contents of the dictionary in Example 9-3 that three
We can see from the contents of the dictionary in Example 9-3 that three
people have asked the script to look out for words and phrases. Two of
people have asked the script to look out for words and phrases. Two of
-
those people—<tt>dj</tt> and <tt>piers</tt>—have interacted with the
+
those people—<tt>dj</tt> and <tt>piers</tt>—have interacted with the
script directly by sending a ''normal'' (or <tt>chat</tt>)
script directly by sending a ''normal'' (or <tt>chat</tt>)
<tt>&lt;message/&gt;</tt>. The other person, with the conference
<tt>&lt;message/&gt;</tt>. The other person, with the conference
Line 608: Line 609:
The two types of incoming messages we're expecting this script to
The two types of incoming messages we're expecting this script to
-
receive are those conveying the room's conversation—in
+
receive are those conveying the room's conversation—in
-
<tt>groupchat</tt>-type messages—and those over which the commands such
+
<tt>groupchat</tt>-type messages—and those over which the commands such
as watch and ignore are carried, which we expect in the form of
as watch and ignore are carried, which we expect in the form of
<tt>normal</tt>- or <tt>chat</tt>-type messages.
<tt>normal</tt>- or <tt>chat</tt>-type messages.
Line 803: Line 804:
high-level function to get at this tag from the <tt>Presence</tt> object
high-level function to get at this tag from the <tt>Presence</tt> object
(in <tt>prs</tt>), but we can strip away the presence object "mantle"
(in <tt>prs</tt>), but we can strip away the presence object "mantle"
-
and get at the underlying object, which is a neutral "node"—a Jabber
+
and get at the underlying object, which is a neutral "node"—a Jabber
element, or XML fragment, without any preconceived ideas of what it is
element, or XML fragment, without any preconceived ideas of what it is
(and therefore without any accompanying high-level methods such as
(and therefore without any accompanying high-level methods such as
Line 815: Line 816:
</code>
</code>
-
The <tt>asNode()</tt> method gives us what we need—a <tt>Protocol</tt>
+
The <tt>asNode()</tt> method gives us what we need—a <tt>Protocol</tt>
object representation of the <tt>&lt;presence/&gt;</tt> element. From
object representation of the <tt>&lt;presence/&gt;</tt> element. From
this we can get to the <tt>&lt;error/&gt;</tt> tag and its contents. If
this we can get to the <tt>&lt;error/&gt;</tt> tag and its contents. If
Line 888: Line 889:
<tt>jdev@conference.jabber.org/qmacro</tt> and there is no room occupant
<tt>jdev@conference.jabber.org/qmacro</tt> and there is no room occupant
in the ''jdev'' room with the nickname <tt>qmacro</tt>, the message will
in the ''jdev'' room with the nickname <tt>qmacro</tt>, the message will
-
still reach its ''first'' destination—the component—but be rejected at
+
still reach its ''first'' destination—the component—but be rejected at
that stage, as shown in Example 9-5.
that stage, as shown in Example 9-5.
Line 903: Line 904:
</code>
</code>
-
Although the rejection—the "Not Found" error—is the same as if a message
+
Although the rejection—the "Not Found" error—is the same as if a message
had been sent to a JSM user that didn't exist, the difference is that
had been sent to a JSM user that didn't exist, the difference is that
the transient user always had the ''potential'' to exist, whereas the
the transient user always had the ''potential'' to exist, whereas the
Line 963: Line 964:
</code>
</code>
-
we also construct—from the <tt>Room</tt>, <tt>ConfServ</tt>, and
+
we also construct—from the <tt>Room</tt>, <tt>ConfServ</tt>, and
-
<tt>Nick</tt> variables—and send the <tt>&lt;presence/&gt;</tt> element
+
<tt>Nick</tt> variables—and send the <tt>&lt;presence/&gt;</tt> element
for negotiating entry to the ''jdev'' room hosted by the Conferencing
for negotiating entry to the ''jdev'' room hosted by the Conferencing
component at <tt>conference.jabber.org</tt>:
component at <tt>conference.jabber.org</tt>:
Line 1,016: Line 1,017:
[[image:jab_0901.png|The LEGO MINDSTORMS RCX, or "programmable brick"|center|350 px]]
[[image:jab_0901.png|The LEGO MINDSTORMS RCX, or "programmable brick"|center|350 px]]
<br></code> There are plenty of ways to
<br></code> There are plenty of ways to
-
interact with the RCX. The MINDSTORMS Robotics Invention System (RIS)™
+
interact with the RCX. The MINDSTORMS Robotics Invention System (RIS)™
set comes with Windows software with which you can build programs by
set comes with Windows software with which you can build programs by
moving blocks of logic around graphically on the screen and chaining
moving blocks of logic around graphically on the screen and chaining
them together. In addition, various efforts on the parts of talented
them together. In addition, various efforts on the parts of talented
individuals have come up with many different ways to program the RCX.
individuals have come up with many different ways to program the RCX.
-
''The Unofficial Guide to LEGO® MINDSTORMS™ Robots'' (O'Reilly &amp;
+
''The Unofficial Guide to LEGO® MINDSTORMSâÃÃÂ�
-
Associates, Inc., 1999) tells you all you need to know about programing
+
-
the RCX. What's important to know for this recipe is detailed in
+
-
Programming the RCX.
+
-
 
+
-
 
+
-
{{Sidebar|Programming the RCXThere are two approaches to programming the
+
-
RCX. One approach is to write a program on your PC, download it to the
+
-
RCX, and start and stop the program using the buttons on the RCX itself.
+
-
 
+
-
The other approach is to control the RCX directly from a program that
+
-
you write ''and'' execute on your PC, sending control signals and
+
-
receiving sensor values over the IR connection.
+
-
 
+
-
Both approaches have their merits. How appropriate each one is boils
+
-
down to one thing: connections. On the one hand, building autonomous
+
-
machines that find their way around the kitchen to scare the cat and
+
-
bring you a sandwich calls for the first approach, when, once you've
+
-
downloaded the program to the RCX, you can dispense with any further
+
-
connection with your PC because the entire logic is situated in your
+
-
creation. On the other hand, if you want to build a physical extension
+
-
to a larger system that, for example, has a connection to the Internet,
+
-
the second approach is likely to be more fruitful, because you can
+
-
essentially use the program that runs on your PC and talks to the RCX
+
-
over the IR link as a conduit, a proxy of sorts, to other programs and
+
-
systems that can be reached over the network. We're going to use the
+
-
second approach.
+
-
 
+
-
The RIS software that comes as standard centers around an ActiveX
+
-
control. While there are plenty of ways to talk to the RCX without using
+
-
this control (the book mentioned earlier describes many of these ways),
+
-
the features offered by the control—<tt>Spirit.ocx</tt>—are fine for
+
-
many a project. And with Perl's <tt>Win32::OLE</tt> module, we can
+
-
interact with this ActiveX control without having to resort to Visual
+
-
Basic.
+
-
 
+
-
</code>
+
-
 
+
-
<br>
+
-
=== What We're Going to Do ===
+
-
<br>Everyone knows that one of the virtues of
+
-
a programmer is ''laziness''. We're going to extend this virtue (perhaps
+
-
a little too far) and enhance it with a hacker's innate ability to
+
-
combine two favorite pastimes—programming and playing with LEGO—to build
+
-
contrived but fun devices.
+
-
 
+
-
Often being a key part of a programmer's intake, coffee figures highly
+
-
on the daily agenda. It's important to have a good cup of coffee to keep
+
-
the brain cells firing, but it's even more important to know whether
+
-
there's actually any coffee left in the pot. Going over to the coffeepot
+
-
to find out is time away from the keyboard and therefore time wasted. So
+
-
let's put the RCX to good use and build a device to tell us, via Jabber,
+
-
whether the coffeepot has enough for another cup.
+
-
 
+
-
In building the device, a light sensor was connected to the RCX to "see"
+
-
the level of coffee in the pot. Since the coffeepot is made of glass,
+
-
light passes through it unless the coffee gets in the way, thus creating
+
-
a simple binary switch:
+
-
 
+
-
* No (or a small amount of) light measured: there's coffee in the pot.
+
-
* Some (or a larger amount of) light: there's no coffee in the pot. We
+
-
want to be able to send the availability of coffee to all interested
+
-
parties in a way that their off-the-shelf Jabber clients can easily
+
-
understand and display.
+
-
 
+
-
Figure 9-2 shows the LEGO MINDSTORMS device in action. The brick mounted
+
-
on the gantry is the light sensor, which extends to the glass coffeepot;
+
-
a wire runs from it to the connector on the RCX. Behind the RCX is the
+
-
IR tower, which is connected to the PC.
+
-
 
+
-
 
+
-
[[image:jab_0902.png|Our device "looking" at the coffeepot|center|350 px]]
+
-
</code> Remembering that
+
-
<tt>&lt;presence/&gt;</tt> elements are a simple way of broadcasting
+
-
information about availability ''and'' that they contain a
+
-
<tt>&lt;status/&gt;</tt> tag to describe the detail or context of that
+
-
availability (see Section 5.4.2 for details on the
+
-
<tt>&lt;presence/&gt;</tt> element), we have a perfect mechanism that's
+
-
ready to be used. What's more, most, if not all, of the off-the-shelf
+
-
Jabber client implementations will display the content of the
+
-
<tt>&lt;status/&gt;</tt> tag in the client user's roster next to the JID
+
-
to which it applies. Figure 9-3 shows how the content of the
+
-
<tt>&lt;status/&gt;</tt> tag is displayed as a hovering "tooltip" in
+
-
WinJab.
+
-
 
+
-
 
+
-
[[image:jab_0903.png|Receiving information on the coffee's status in WinJab|center|350 px]]
+
-
</code> Here's what we need to do:
+
-
 
+
-
 
+
-
; Step 1: Set up the RCX
+
-
: We need to set the RCX up, with the light sensor, so that it's close
+
-
: enough to the coffeepot to take reliable and consistent light
+
-
: readings. Luckily the serial cable that comes with the MINDSTORMS set
+
-
: and connects to the IR tower is long enough to stretch from the
+
-
: computer to within the infrared line of sight to the RCX.
+
-
; Step 2: Make the correct calibrations
+
-
: There are bound to be differences in ambient light, sensitivity of the
+
-
: light sensor, and how strong you make your coffee. So we need a way of
+
-
: calibrating the setup, so that we can find the appropriate "pivot
+
-
: point" light reading value that lies between the two states of
+
-
: ''coffee'' and ''no coffee''.
+
-
; Step 3: Set up a connection to Jabber
+
-
: We need a connection to a Jabber server and a client account there. We
+
-
: can set one up using the reguser script from Section 7.4. We also need
+
-
: the script to honor presence from users who want to be informed of the
+
-
: coffee state.
+
-
; Step 4: Set up a sensor poll/presence push loop
+
-
: Once the RCX has been set up, the sensor calibrations taken, and the
+
-
: connection has been made, we need to monitor the light sensor on the
+
-
: RCX at regular intervals. At each interval, we determine the coffee
+
-
: state by comparing the value received from the sensor with the pivot
+
-
: point determined in the calibration step and send any change in that
+
-
: state as a new availability <tt>&lt;presence/&gt;</tt> element
+
-
: containing an appropriate description in the <tt>&lt;status/&gt;</tt>
+
-
: tag.
+
-
<br>
+
-
=== The Coffee Script ===
+
-
<br>We're going to use Perl and the
+
-
<tt>Net::Jabber</tt> libraries to build the script shown in Example 9-6.
+
-
Perl allows us a comfortable way to interact with an ActiveX control,
+
-
through the <tt>Win32::OLE</tt> module, so let's have a look at the
+
-
''coffee'' script as a whole, then we'll go back and look at the script
+
-
in detail.
+
-
 
+
-
 
+
-
''The coffee script, written in Perl''
+
-
 
+
-
<code>use Net::Jabber qw(Client); use Win32::OLE; use Getopt::Std; use
+
-
strict;
+
-
 
+
-
my %opts; getopt('ls', \%opts);
+
-
 
+
-
use constant SERVER =&gt; "merlix.dyndns.org"; use constant PORT
+
-
=&gt; 5222; use constant USERNAME =&gt; "coffee"; use constant PASSWORD
+
-
=&gt; "pass"; use constant RESOURCE =&gt; "perlscript";
+
-
 
+
-
use constant NOCOFFEE =&gt; 0; use constant COFFEE =&gt; 1;
+
-
 
+
-
use constant SENSOR =&gt; defined($opts{'s'}) ? $opts{'s'} : 0; use
+
-
constant GRAIN =&gt; 1;
+
-
 
+
-
my $current_status = -1; my @status; $status[NOCOFFEE] = 'xa/Coffeepot
+
-
is empty'; $status[COFFEE] = '/Coffee is available!';
+
-
 
+
-
my $rcx = &amp;setup_RCX(SENSOR);
+
-
 
+
-
# Either calibrate if no parameters given, or run with the parameter
+
-
# given as -l, which will be taken as the pivot between coffee and no
+
-
# coffee
+
-
&amp;calibrate($rcx) unless defined($opts{'l'});
+
-
 
+
-
# Determine initial status (will be either 0 or 1)
+
-
my $s = &amp;set_status($rcx-&gt;Poll(9, SENSOR));
+
-
 
+
-
my $jabber = &amp;setup_Jabber(SERVER, PORT, USERNAME, PASSWORD,
+
-
RESOURCE, $s);
+
-
 
+
-
# Main loop: check Jabber and RCX
+
-
while (1) { defined($jabber-&gt;Process(GRAIN)) or die "The connection
+
-
to the Jabber server was broken\n"; my $s =
+
-
&amp;set_status($rcx-&gt;Poll(9, SENSOR)); &amp;set_presence($jabber,
+
-
$s) if defined $s;
+
-
}
+
-
 
+
-
 
+
-
# Set up Jabber client connection, sending initial presence
+
-
sub setup_Jabber { my ($server, $port, $user, $pass, $resource,
+
-
$initial_status) = @_; my $connection = new Net::Jabber::Client;
+
-
 
+
-
# Connect
+
-
my $status = $connection-&gt;Connect( hostname =&gt; $server, port
+
-
=&gt; $port ); die "Cannot connect to Jabber server $server on port
+
-
$port\n" unless $status;
+
-
 
+
-
# Callbacks
+
-
$connection-&gt;SetCallBacks( presence =&gt; \&amp;InPresence );
+
-
 
+
-
# Ident/Auth
+
-
my @result = $connection-&gt;AuthSend( username =&gt; $user, password
+
-
=&gt; $pass, resource =&gt; $resource ); die "Ident/Auth failed:
+
-
$result[0] - $result[1]\n" if $result[0] ne "ok";
+
-
 
+
-
# Roster
+
-
$connection-&gt;RosterGet();
+
-
 
+
-
# Initial presence dependent upon initial status
+
-
&amp;set_presence($connection, $initial_status);
+
-
 
+
-
return $connection;
+
-
}
+
-
 
+
-
 
+
-
sub set_presence { my ($connection, $s) = @_; my $presence =
+
-
Net::Jabber::Presence-&gt;new(); my ($show, $status) = split("/",
+
-
$status[$s], 2); $presence-&gt;SetPresence( show =&gt; $show, status
+
-
=&gt; $status ); print $status, "\n"; $connection-&gt;Send($presence);
+
-
}
+
-
 
+
-
 
+
-
# Handle presence messages
+
-
sub InPresence { my $presence = $_[1]; my $from =
+
-
$presence-&gt;GetFrom(); my $type = $presence-&gt;GetType();
+
-
 
+
-
if ($type eq "subscribe") { print "Subscribe request ($from) ...\n";
+
-
$jabber-&gt;Send($presence-&gt;Reply(type =&gt; 'subscribed'));
+
-
}
+
-
 
+
-
if ($type eq "unsubscribe") { print "Unsubscribe request ($from)
+
-
...\n"; $jabber-&gt;Send($presence-&gt;Reply(type =&gt;
+
-
'unsubscribed'));
+
-
}
+
-
}
+
-
 
+
-
 
+
-
sub setup_RCX { my $sensor = shift; my $rcx =
+
-
Win32::OLE-&gt;new('SPIRIT.SpiritCtrl.1'); $Win32::OLE::Warn = 0;
+
-
$rcx-&gt;{ComPortNo} = 1; $rcx-&gt;{InitComm};
+
-
$rcx-&gt;SetSensorType($sensor, 3); $rcx-&gt;SetSensorMode($sensor, 2);
+
-
return $rcx;
+
-
}
+
-
 
+
-
 
+
-
sub calibrate { my $rcx = shift;
+
-
 
+
-
print &lt;&lt;EOT; Calibration mode. Note the sensor values and decide
+
-
on a 'pivot' value above which 'no coffee' is signified and below
+
-
which 'coffee' is signified.
+
-
 
+
-
End the calibration mode with Ctrl-C.
+
-
 
+
-
Press Enter to start calibration... EOT
+
-
 
+
-
&lt;STDIN&gt;;
+
-
 
+
-
while (1) { print $rcx-&gt;Poll(9, SENSOR), " "; sleep 1;
+
-
}
+
-
 
+
-
}
+
-
 
+
-
 
+
-
sub set_status { my $val = shift;
+
-
 
+
-
my $new_status = $val &lt; $opts{'l'} ? COFFEE : NOCOFFEE;
+
-
 
+
-
if ($new_status != $current_status) { $current_status = $new_status;
+
-
return $current_status;
+
-
}
+
-
else { return undef;
+
-
}
+
-
}
+
-
 
+
-
</code>
+
-
 
+
-
<br>
+
-
=== Examining the Coffee Script Step by Step ===
+
-
<br>Now that we've seen the ''coffee'' script as a whole, let's examine it step by step to see how
+
-
it works.
+
-
 
+
-
<br>
+
-
 
+
-
==== Declaring the modules, constants, and variables ====
+
-
<br>We first
+
-
declare the packages we're going to use. In addition to
+
-
<tt>Net::Jabber</tt> and <tt>Win32::OLE</tt>, we're going to use
+
-
<tt>Getopt::Std</tt>, which affords us a comfortable way of accepting
+
-
and parsing command-line options. We also want to use the
+
-
<tt>strict</tt> pragma, which should keep us from making silly coding
+
-
mistakes by not allowing undeclared variables and the like.
+
-
 
+
-
We specify <tt>Client</tt> on the usage declaration for the
+
-
<tt>Net::Jabber</tt> package to specify what should be loaded. The
+
-
package is a large and comprehensive set of modules, and only some of
+
-
those are relevant for what we wish to do in the script—build and work
+
-
with a Jabber ''client'' connection. Other module sets are pulled in by
+
-
specifying <tt>Component</tt> or <tt>Server</tt>.
+
-
 
+
-
 
+
-
<code>use Net::Jabber qw(Client); use Win32::OLE; use Getopt::Std; use
+
-
strict;
+
-
 
+
-
</code>
+
-
We're going to allow the command-line options -l and -s, which perform
+
-
the following tasks:
+
-
 
+
-
 
+
-
; No options specified (or just the -s options): calibration mode.
+
-
: When we run the script for the first time, we need to perform the
+
-
: calibration and read values from the sensor to determine a midpoint
+
-
: value. A number above the midpoint signifies the presence of light
+
-
: and therefore the absence of coffee; below signifies the absence of
+
-
: light and therefore the presence of coffee. This step is necessary
+
-
: because not every environment (ambient light, sensitivity of the light
+
-
: sensor, and so on) will be the same. The upper and lower values,
+
-
: representing lightness and darkness, respectively, will vary across
+
-
: different environments. The point is to obtain a value in between
+
-
: these upper and lower values—the midpoint—with which we can compare a
+
-
: light value read at any particular time. : If we don't specify any
+
-
: options, the script will start up automatically in calibration mode:
+
-
<code>C:\temp&gt; <tt>perl coffee.pl</tt></code>
+
-
: Figure 9-4 shows the script run in calibration mode. The values
+
-
: displayed, one each second, represent the values read from the light
+
-
: sensor. When the sensor was picking up lots of light, the values were
+
-
: 60. When the sensor was moved in front of some coffee, the values went
+
-
: down to around 45. Based upon this small test, the pivot point value
+
-
: was 50, somewhere in between those two values.
+
-
; -l: Specify the pivot value.
+
-
: Once we've determined a pivot point value, we run the script and tell
+
-
: it this pivot value with the -l (light pivot):
+
-
<code>C:\temp&gt; <tt>perl coffee.pl -l 50</tt></code>
+
-
 
+
-
; -s: Specify the sensor number.
+
-
: The RCX, shown in Figure 9-1, has three connectors to which you can
+
-
: attach sensors. They're the three gray 2-by-2 pieces, labeled 1, 2,
+
-
: and 3, near the top of the brick. The script assumes you've attached
+
-
: the light sensor to the one marked 1, which internally is 0. If you
+
-
: attach it to either of the other two, you can specify the connector
+
-
: using the -s (sensor) with a value of 1 (for the middle connector) or
+
-
: 2 (for the rightmost connector), like this:
+
-
<code>C:\temp&gt; <tt>perl coffee.pl -l 50 -s 2</tt></code>
+
-
: You can specify the -s option when running in calibration or normal
+
-
: modes.
+
-
{{Figure|title=Running coffee in calibration
+
-
mode|image=0596002025-jab_0904.png</code> The options, summarized in Table
+
-
9-1, are defined with the <tt>Getopt::Std</tt> function:
+
-
 
+
-
 
+
-
<code>my %opts; getopt('ls', \%opts);
+
-
 
+
-
</code>
+
-
 
+
-
{|
+
-
 
+
-
|+ Summary of the startup options -
+
-
! Option !! Meaning
+
-
|-
+
-
| No option || Start the script automatically in calibration mode.
+
-
|-
+
-
| -l || Specify the pivot value.
+
-
|-
+
-
| -s || Specify the sensor number.
+
-
|}
+
-
Next comes a list of constants, which describe:
+
-
 
+
-
* The script's Jabber relationship, including the server it will connect
+
-
to and the username, password, and resource it will connect with.
+
-
* The representation of the two states of ''coffee'' and ''no coffee'', which
+
-
will be used to determine the content of the <tt>&lt;status/&gt;</tt>
+
-
tag sent along inside any <tt>&lt;presence/&gt;</tt> element emitted.
+
-
* The identification of the connector to which the light sensor is
+
-
attached and the polling granularity of the sensor (poll/presence push
+
-
loop) described earlier. This item is measured in seconds.
+
-
 
+
-
<code>use constant SERVER =&gt; "merlix.dyndns.org"; use constant
+
-
PORT =&gt; 5222; use constant USERNAME =&gt; "coffee"; use constant
+
-
PASSWORD =&gt; "pass"; use constant RESOURCE =&gt; "perlscript";
+
-
 
+
-
use constant NOCOFFEE =&gt; 0; use constant COFFEE =&gt; 1;
+
-
 
+
-
use constant SENSOR =&gt; defined($opts{'s'}) ? $opts{'s'} : 0; use
+
-
constant GRAIN =&gt; 1;
+
-
 
+
-
</code>
+
-
The last part of the script's setup deals with the coffee state:
+
-
 
+
-
 
+
-
<code>my $current_status = -1; my @status; $status[NOCOFFEE] =
+
-
'xa/Coffeepot is empty'; $status[COFFEE] = '/Coffee is available!';
+
-
 
+
-
</code>
+
-
We use a two-element array (<tt>@status</tt>) to represent the two
+
-
possible coffee states. The value of each array element is a two-part
+
-
string, with each part separated by a slash (<tt>/</tt>). Each of these
+
-
parts will be transmitted in a <tt>&lt;presence/&gt;</tt> element, with
+
-
the first part (which is empty in the element representing the
+
-
<tt>COFFEE</tt> state) representing the presence <tt>&lt;show/&gt;</tt>
+
-
value and the second part representing the presence
+
-
<tt>&lt;status/&gt;</tt> value. Example 9-7 shows what a
+
-
<tt>&lt;presence/&gt;</tt> element looks like when built up with values
+
-
to represent the <tt>NOCOFFEE</tt> state.
+
-
 
+
-
 
+
-
''A presence element representing the NOCOFFEE state''
+
-
 
+
-
<code>&lt;presence&gt; &lt;show&gt;xa&lt;/show&gt;
+
-
&lt;status&gt;Coffeepot is empty&lt;/status&gt; &lt;/presence&gt;
+
-
 
+
-
</code>
+
-
Most Jabber clients use different icons in the roster to represent
+
-
different <tt>&lt;show/&gt;</tt> values. In this case, we will use
+
-
<tt>xa</tt> for ''no coffee'' and a blank (which represents "online" or
+
-
"available") for ''coffee'' to trigger the icon change.
+
-
 
+
-
<br>
+
-
 
+
-
==== Initialization and calibration ====
+
-
<br>Whenever we need to talk to the
+
-
RCX, some initialization is required via the ActiveX control. That's the
+
-
same whether we're going to calibrate or poll for values. The
+
-
<tt>setup_RCX()</tt> function takes a single argument—the identification
+
-
of which connector the light sensor is connected to—and performs the
+
-
initialization, which is described later in Section 9.2.3.8. The
+
-
function returns a handle on the <tt>Win32::OLE</tt> object that
+
-
represents the ActiveX control, which in turn represents the RCX via the
+
-
IR tower:
+
-
 
+
-
 
+
-
<code>my $rcx = &amp;setup_RCX(SENSOR);
+
-
 
+
-
</code>
+
-
If the -l option is not specified, it means we're going to be running
+
-
calibration. So we call the <tt>calibrate()</tt> function to do this for
+
-
us. We pass the RCX handle (in <tt>$rcx</tt>) so the calibration can run
+
-
properly:
+
-
 
+
-
 
+
-
<code># Either calibrate if no parameters given, or
+
-
# run with the parameter given as -l, which will be taken as the pivot
+
-
# between coffee and no coffee
+
-
&amp;calibrate($rcx) unless defined($opts{'l'});
+
-
 
+
-
</code>
+
-
As with the <tt>setup_RCX()</tt> function, <tt>calibrate()</tt> is
+
-
described later.
+
-
 
+
-
Calibration mode will be terminated by ending the script with
+
-
''Ctrl-C'', so the next thing we come across is the call to the function
+
-
<tt>set_status()</tt>, which represents the first stage in the normal
+
-
script mode; <tt>set_status()</tt> is used to determine the ''initial''
+
-
coffee status.
+
-
 
+
-
A value is retrieved by calling the ActiveX control's <tt>Poll()</tt>
+
-
function. (Table 9-2 lists the ActiveX control's functions and
+
-
properties used in this script.) We specify that we're after a sensor
+
-
value (the 9 as the first argument) from the sensor attached to the
+
-
connector indicated by the <tt>SENSOR</tt> constant:
+
-
 
+
-
 
+
-
<code># Determine initial status (will be either 0 or 1) my $s =
+
-
&amp;set_status($rcx-&gt;Poll(9, SENSOR));
+
-
 
+
-
</code>
+
-
The value retrieved is passed to the <tt>set_status()</tt> function,
+
-
which determines whether the value is above or below the pivot value and
+
-
whether the new status is different from the current one. It's going to
+
-
be something along the lines of one of the values displayed when the
+
-
script was run in calibration mode. If it is (and in this case, it will
+
-
be, because in this first call, the value of <tt>$current_status</tt> is
+
-
set to <tt>-1</tt>, which represents neither the <tt>COFFEE</tt> nor the
+
-
<tt>NOCOFFEE</tt> state), that status will be returned; otherwise,
+
-
<tt>undef</tt> will be returned.
+
-
 
+
-
 
+
-
{|
+
-
 
+
-
|+ RCX Spirit.ocx ActiveX control properties and functions used -
+
-
! Function/Property !! Description
+
-
|-
+
-
| <tt>Poll(''SOURCE'', ''NUMBER'')</tt> || Retrieves information from
+
-
| the RCX. In this script, the value for the ''SOURCE'' argument is
+
-
| always <tt>9</tt>, which represents a sensor value (i.e., a value
+
-
| measured at a sensor), as opposed to an internal RCX variable or a
+
-
| timer. The ''NUMBER'' argument represents the connector to which the
+
-
| sensor we want to read is attached.
+
-
|-
+
-
| <tt>SetSensorMode(''NUMBER'', ''MODE ''[, ''SLOPE''])</tt> || This
+
-
| function returns a value from the sensor. As with <tt>Poll()</tt> and
+
-
| <tt>SetSensorType()</tt>, ''NUMBER'' represents the sensor connector.
+
-
| The ''MODE'' argument can be used to determine the sensor mode, which
+
-
| can be ''Raw'' (mode 0), ''Boolean'' (mode 1), ''Transitional'' (mode
+
-
| 2), ''Periodic'' (mode 3), ''Percentage'' (mode 4), ''Celcius'' (mode
+
-
| 5), ''Farenheit'' (mode 6), or ''Angle'' (mode 7). The ''SLOPE''
+
-
| argument qualifies the ''Boolean'' mode by specifying how ''True'' and
+
-
| ''False'' are to be determined.
+
-
|-
+
-
| <tt>SetSensorType(''NUMBER'', ''TYPE'')</tt> || This function is used
+
-
| to specify the ''type'' of sensor the values will be read from. The
+
-
| ''NUMBER'' argument is the same as for the <tt>Poll()</tt> and
+
-
| represents the sensor connector. The ''TYPE'' argument represents the
+
-
| type of sensor that you want to set: ''None'' (type 0), ''Switch''
+
-
| (type 1), ''Temperature'' (type 2), ''Light'' (type 3), or ''Angle''
+
-
| (type 4).
+
-
|-
+
-
| <tt>property:ComPortNo</tt> || The serial port to which the IR tower
+
-
| is connected (e.g., 1 = COM1, 2 = COM2, and so on).
+
-
|-
+
-
| <tt>property:InitComm</tt> || When invoked, the serial communication
+
-
| port is initialized in preparation for the IR connection to the RCX.
+
-
|}
+
-
 
+
-
<br>
+
-
==== Connecting to the Jabber server ==== <br>
+
-
At this stage, we're ready to
+
-
connect to the Jabber server. The call to <tt>setup_Jabber()</tt> does
+
-
this for us, returning a handle to the Jabber connection object that we
+
-
store in <tt>$jabber</tt>. This handle will be used later in the script
+
-
to send out <tt>&lt;presence/&gt;</tt> elements. The <tt>$jabber</tt>
+
-
variable contains a reference to a <tt>Net::Jabber::Client</tt> object.
+
-
This is the equivalent of the <tt>con</tt> variable used in the earlier
+
-
Python scripts to hold the <tt>jabber.Client</tt> object and the
+
-
<tt>ConnectionBean</tt> object (<tt>cb</tt>) in the earlier Java
+
-
script.<ref>For the Python scripts, see Section 8.1 and Section 8.3. For
+
-
the Java script, see Section 8.2.</ref>
+
-
 
+
-
 
+
-
<code>my $jabber = &amp;setup_Jabber(SERVER, PORT, USERNAME, PASSWORD,
+
-
RESOURCE, $s);
+
-
 
+
-
</code>
+
-
In addition to passing the constants needed for the client connection to
+
-
the Jabber server, we pass the initial coffee status, held in
+
-
<tt>$s</tt>. We'll have a look at what the <tt>setup_Jabber()</tt>
+
-
function does with this initial status a bit later when we get to the
+
-
function's definition.
+
-
 
+
-
<br>
+
-
 
+
-
==== Sensor poll/presence push loop ====
+
-
<br>Now that we've set everything
+
-
up, determined the initial coffee status, and connected to the Jabber
+
-
server, we're ready to start the main loop:
+
-
 
+
-
 
+
-
<code># Main loop: check Jabber and RCX while (1) {
+
-
defined($jabber-&gt;Process(GRAIN)) or die "The connection to the Jabber
+
-
server was broken\n"; my $s = &amp;set_status($rcx-&gt;Poll(9, SENSOR));
+
-
&amp;set_presence($jabber, $s) if defined $s;
+
-
}
+
-
 
+
-
</code>
+
-
The <tt>while (1)</tt> loop is a bit of a giveaway. This script won't
+
-
stop until you force it to by entering ''Ctrl-C''—but that's essentially
+
-
what we want. In the loop, we call the <tt>Process()</tt> method on the
+
-
Jabber connection object in <tt>$jabber</tt>.
+
-
 
+
-
<tt>Process()</tt> is the equivalent of the <tt>Jabberpy</tt>'s
+
-
<tt>process()</tt> method in the Python scripts. <tt>Process()</tt>
+
-
waits around for up to the number of seconds specified as the single
+
-
argument (or not at all if no argument is specified) for XML to appear
+
-
on the stream connection from the Jabber server. If complete fragments
+
-
do appear, callbacks, defined in the connection object, are called with
+
-
the elements (<tt>&lt;iq/&gt;</tt>, <tt>&lt;message/&gt;</tt>, and
+
-
<tt>&lt;presence/&gt;</tt>) that the fragments represent. This is in the
+
-
same way as, for example, callbacks are used in the Python scripts using
+
-
the <tt>Jabberpy</tt> library. The <tt>setup_Jabber()</tt>, which will
+
-
be discussed in the next section, is where the callback definition is
+
-
made.
+
-
 
+
-
<tt>Net::Jabber</tt>'s <tt>Process()</tt> method returns <tt>undef</tt>
+
-
if the connection to the Jabber server is terminated while waiting for
+
-
XML. The <tt>undef</tt> value is dealt with appropriately by ending the
+
-
script.
+
-
 
+
-
The <tt>GRAIN</tt> constant, set to 1 second in the script's setup
+
-
section, is used to specify how long to wait for any packets from the
+
-
Jabber server. For the most part, we're not expecting to receive much
+
-
incoming Jabber traffic—the occasional presence subscription (or
+
-
unsubscription) request perhaps (see later), but other than that, the
+
-
only packets traveling over the connection to the Jabber server will be
+
-
availability <tt>&lt;presence/&gt;</tt> packets representing coffee
+
-
state changes, sent from the script. This delay is normally set to 1
+
-
second. And because that's a comfortable polling interval for the light
+
-
sensor, we can set that within the same loop.
+
-
 
+
-
Calling the ActiveX control's <tt>Poll()</tt> again with the same
+
-
arguments as before ("get a sensor value from the sensor attached to the
+
-
<tt>SENSOR</tt>th connector"), we pass the value to the
+
-
<tt>set_status()</tt> to determine the coffee state. If the state was
+
-
different from last time (if <tt>$s</tt> receives a value and not
+
-
<tt>undef</tt>), then we want to emit a <tt>&lt;presence/&g
+

Revision as of 01:53, 1 August 2009

silent movie theater on fairfax american express money wire las vegas poker tournaments february printed sweatshirts gazebo designs wwe diva stacy keibler. razzle map iowa city entertainment walley fishing manitoba site indecent proposal video clip free gd55 mobile panasonic ringtone moms movies.com a perfect circle music video downloads amateur submitted movies auto nationwide watch.com marisa miller video free mobile panasonic ringtone movie schedule tinseltown sponges phylum popup blockers russian mature movies secretely music roger video zapp nude madonna dan post boots sitemap annie lennox ringtone framed photos movie clip heather graham link mass transit magazines coach replica amr ringtones free download english to german masterbate clit electric burr grinder pharmacy ethnic video sexual intercourse photos deer hunting igrice video microsoft open license site nostalgiques stickers russian movie download movie icon avatars anna ohura videos phentermine pharmacy biz wet nurses white water rafting wales uk playstation3 merman movies ritalin adderall concerta sitemap decadron preview torrent file mpeg4 movie converter asia movie trailer craigs list denver irish drink mix 2005 draft nhl media player movie trailors leonard maltin movie reviews apache mysql php windows installer hohner nik sharpener pro serial index link penneys radiator hose wu-tang clan woman naked lowden guitar serial numbers recurso humanos matthew perry drug addiction kill myself eminem puke video easy producer video aspiration book guest lex on blondes domain oxycontin lil wyte lyrics what is extacy drug pretty ricky video total wine naked photo post adam brody jimmy kimmel best way to lose weight magnum movie p.i infant blood pressure normal plastic license plate blanks allfreegay trackpad powerbook adobe photoshop 8 cs cracked interpreter memphis homes el nino red envolope irene hunt pictures pussy eating gallery free web design template pwl cracker download activation end process product software cell motorola phone razr ringtone showplace 16 coon rapids mn url in nevada pharmacy school sexy seamed stockings sample sex video and movie speculumpages archive video files free ringtones alltel audiovox nastyrat.com marmalade mr review lolita index alive blaine clip david drowned video pioneer press page aurora fox mall valley 2 best camera digital megapixel links eating a balanced diet adultactioncam.com cardizem cd anabolic steroids actos altace robert palmer simply irresistible music video girls messy cheerleader pacific assault no cd crack shemale domination lunchboxes live night rap saturday video karate kata video free ohiolottery rakuen movie 4 inuyasha smoothmag wholesale home tanning bed honda radiator skydiving video clip movie lobby card index environmental sampling supplies valassis ivp care pharmacy pure country movie quotes student housing chicago cubs index pictures of rainbows top suffolk realty agents

Contents

Groupchat, : Components, and Event Models


By now, you should have a

good idea of how scripts interact with Jabber and how the core
elements such as <message/> and
<presence/> can be constructed and handled.

This chapter builds upon what we've already seen in Chapter 8 and introduces new concepts. We build a nosy assistant that joins a conference room and alerts us to words and phrases that we want it to listen for. There are two popular conference protocols, as mentioned in Section 6.2.6—the presence-based Groupchat protocol, and the jabber:iq:conference-based Conference protocol. The assistant recipe, a foray into the world of 'bots, takes a look at the original presence-based one.

As we've seen, programming within Jabber's event model is fairly straightforward. But what happens when you want to meld other components with event models of their own? We look at a couple of typical scenarios where this melding needs to happen. The first is a homage to the Trojan Room Coffee Machine (http://www.cl.cam.ac.uk/coffee/coffee.html), where we give life, or at least presence, to a coffeepot, using LEGO® MINDSTORMS™. The second is a Tk-based RSS headline viewer. Both the coffeepot and the Tk programming library have event loops of their own. With the coffeepot, we need to have a loop that polls the coffeepot's status, independently of the polling for incoming packets from the Jabber server. The Tk programming library's event model is similar to those of the Jabber programming libraries that are used in the recipes in this book, in that handlers are set up and a loop is started that listens for UI events. In both cases, we need to get these event loops to work in harmony with the Jabber libraries' event loops.

Building Jabber solutions without event loops is a Sisyphean task. The very nature of Jabber communication is event-based, and it's important to understand how to use the event features of the Jabber programming libraries and also how to be able to mesh those features with similar features in other libraries and systems.

We also look at extending messages and build a mechanism that delivers RSS headlines to clients who register with that mechanism. These headlines are carried using an extended message type. In fact, the RSS Delivery Mechanism is a component. The three recipes in Chapter 8, were Jabber clients, in that they connected to the Jabber network via the Jabber Session Manager (JSM) service. We look at the differences between programming a client and programming a component in this chapter and build a complete component that can be queried and interacted with using the third of Jabber's building blocks—the <iq/> element.

Happy coding!


Keyword Assistant


Many of the Jabber core and peripheral developers hang out in a conference room called jdev hosted by the Conferencing component on the Jabber server running at jabber.org. While a lot of useful information is to be gleaned from listening to what goes on in jdev, it isn't possible to be there all the time. Conversations in jdev are logged to web pages, which can be used to visit after the fact to try to catch up with things; however, this can be a hopeless task. One solution is to build a 'bot that looks for specific keywords and Uniform Resource Locators (URLs) in the conversations in jdev and send those on as Jabber messages.

This script, keyassist, connects to a Jabber server, enters a conference room, and listens to the conversations, looking for certain words and phrases to be uttered. The keyassist script is given a bit of "intelligence" in that it can be interacted with and told, while running, to watch for (or stop watching for), certain words and phrases.

The keyassist script introduces us to programmatic interaction with the Conferencing component. Before looking at the script, however, let's have a brief overview of Conferencing in general.



Conferencing


The Conferencing component at jabber.org is conference.jabber.org. Details of the component instance configuration for such a Conferencing component can be found in Section 4.10, where we see that the component exists as a shared object library connected with the library load component connection method. This component provides general conferencing facilities, oriented around a conference room and conference user model.

A Jabber user can enter (or join) a conference room, thereby becoming a conference user identified by a nickname that is chosen upon entering that room. Nicknames are generally used in conference rooms to provide a modicum of privacy—it is assumed that by default you don't want to let the other conference room members know your real JID.

The Conferencing component supports two protocols for user and room interaction: a simple one that provides basic features and a more complex one that provides the basic features plus facilities such as password-protected rooms and room descriptions—Groupchat and Conference.


{{Note|There is a third protocol, called Experimentaliq:groupchat, which came between the Groupchat and Conference protocols. This reflected an experimental move to add features to the basic Groupchat protocol using IQ elements, the contents of which were qualified by a namespace jabber:iq:groupchat. This protocol has been dropped, and support for it exists only in certain versions of WinJab and JIM.

</code>

Groupchat
The Groupchat protocol is the simpler of the two and provides basic
functions for entering and exiting conference rooms and choosing
nicknames. : This Groupchat protocol is known as the presence-based
protocol, because the protocol is based upon
<presence/> elements used for room entry, exit, and
nickname determination. The Groupchat protocol has a nominal version
number of 1.0.
Conference
The Conference protocol offers more advanced features than the
Groupchat protocol and makes use of two IQ namespaces:
jabber:iq:conference and jabber:iq:browse. It has a
nominal protocol version number of 1.4, which reflects the version of
the Jabber server with which it is delivered. Sometimes this version
number is referred to as 0.4, such as in the downloadable tarball and
in the value returned in response to a "version query" on the
component itself, as shown in Example 9-1. : The version number isn't
that important. The main thing to keep in mind is that the component
that is called conference.so (see the reference to the
shared object library in Section 4.10.4) supports both the
Groupchat protocol and the Conference protocol. If you come across a
shared object library called groupchat.so, this is the
original Conferencing component that was made available with Jabber
server Version 1.0. This library supports only the Groupchat protocol.

Querying the Conferencing component's version

SEND: <iq type='get' to='conference.gnu.mine.nu'> <query xmlns='jabber:iq:version'/> </iq>

RECV: <iq to='dj@gnu.mine.nu/jarl' from='conference.gnu.mine.nu' type='result'> <query xmlns='jabber:iq:version'> <name>conference</name> <version>0.4</version> <os>Linux 2.4.2-2</os> </query> </iq> In this recipe we'll be using the simpler Groupchat protocol. It's widely used and easy to understand. Example 9-2 shows a typical element log from Groupchat-based activity. It shows a user, with the JID qmacro@jabber.com, entering a room called "cellar," hosted on the conference component at conf.merlix.dyndns.org, a room that currently has two other occupants who go by the nicknames flash and roscoe. The elements are from qmacro's perspective, and are all explained following the example.


The Groupchat protocol in action The user qmacro tries to enter the conference room with the nickname flash and fails:


SEND: <presence to='cellar@conf.merlix.dyndns.org/flash'/>

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash' type='error'> <error code='409'>Conflict</error> </presence>

He tries again, this time with a different nickname, deejay, and is successful:


SEND: <presence to='cellar@conf.merlix.dyndns.org/deejay'/>

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash'/>

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/roscoe'/>

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/deejay'/>

RECV: <message to='qmacro@jabber.com/jarltk' type='groupchat' from='cellar@conf.merlix.dyndns.org'> <body>deejay has become available</body> </message>

roscoe says hi, and qmacro waves back:


RECV: <message to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/roscoe' type='groupchat' cnu=> <body>hi</body> </message>

SEND: <message to='cellar@conf.merlix.dyndns.org' type='groupchat'> <body>/me waves to everyone</body> </message>

flash sends a private message to qmacro:


RECV: <message to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash' type='chat'> <body>Is that you, qmacro?</body> <thread>jarl1998911094</thread> </message>

Feeling left out of the conversation, roscoe leaves the room:


RECV: <presence to='qmacro@jabber.com/jarltk' type='unavailable' from='cellar@conf.merlix.dyndns.org/roscoe'/>

RECV: <message to='qmacro@jabber.com/jarltk' type='groupchat' from='cellar@conf.merlix.dyndns.org'> <body>roscoe has left</body> </message> Let's take the stages in Example 9-2 one by one.


Failed attempt to enter room
qmacro makes an attempt to enter the room using the Groupchat
protocol. This is done by sending a directed
<presence/> element to a particular JID that represents
the room and the chosen nickname. This JID is constructed as follows:

[room name]@[conference component]/[nickname]

In this example, the conferencing component is identified with the
hostname conf.merlix.dyndns.org. qmacro's choice of
nickname is flash:

cellar@conf.merlix.dyndns.org/flash

Thus the following element is sent:

SEND: <presence to='cellar@conf.merlix.dyndns.org/flash'/>

The conference component determines that there is already someone
present in the room cellar@conf.merlix.dyndns.org with the
nickname flash, so qmacro is notified of this and
receives a directed presence with an <error/> tag:

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash' type='error'> <error code='409'>Conflict</error> </presence>

Note that the <presence/> element has the type
error and comes from the artificial JID constructed in the
room entry attempt. The element is addressed to qmacro's real
JID, of course—qmacro@jabber.com/jarltk—as otherwise it
wouldn't reach him. : The error code 409 and text "Conflict" tells
qmacro that the nickname conflicted with one already in the
room. This is a standard error code/text pair; Table 5-3 shows a
complete set of code/text pairs. : At this stage, qmacro is
not yet in the room.
Successful attempt to enter room
qmacro tries again, this time with a different nickname,
deejay:[1]

SEND: <presence to='cellar@conf.merlix.dyndns.org/deejay'/>

This time, there is no conflict—no other user is in the room "cellar"
with that nickname—and the conference component registers the entry.
It does this by sending qmacro the presence of all the room
occupants, including that of himself:

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash'/>

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/roscoe'/>

RECV: <presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/deejay'/>

These presence elements are also sent to the other room occupants so
they know that deejay is present.
Conference component-generated notification
In addition to the presence elements sent for each room occupant, a
general roomwide message noting that someone with the nickname
deejay just entered the room is sent out by the component as
a type='groupchat' message to all the room occupants:

RECV: <message to='qmacro@jabber.com/jarltk' type='groupchat' from='cellar@conf.merlix.dyndns.org'> <body>deejay has become available</body> </message>

The text "has become available" used in the body of the message is
taken directly from the Action Notices definitions, part of the
Conferencing component instance configuration described in Section
4.10.3. Note that the identity of the room itself is simply a generic
version of the JID that the room occupants use to enter:

cellar@conf.merlix.dyndns.org

Roomwide chat
Once the user with the nickname roscoe sees someone enter the
room, he sends a greeting, and qmacro waves back:

RECV: <message to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/roscoe' type='groupchat' cnu=> <body>hi qmacro</body> </message>

SEND: <message to='cellar@conf.merlix.dyndns.org' type='groupchat'> <body>/me waves to everyone</body> </message>

As with the notification message, each message is a
groupchat-type message. The one received appears to come from
cellar@conf.merlix.dyndns.org/roscoe, which is the JID
representing the user in the room with the nickname roscoe.
This way, roscoe's real JID is never sent to qmacro.
The message deejay sends is addressed to the room's identity
cellar@conf.merlix.dyndns.org, and contains a message that
starts with /me. This is simply a convention that is
understood by clients that support conferencing, meant to represent an
action and displayed thus:

* deejay waves to everyone

{{Note|Ignore the cnu attribute; it's put there and used by the component and should never make it out to the client endpoints. The attribute name is a short name for the conference user and refers to the internal structure that represents a conference room occupant within the component.

</code>

One-on-one chat
The Conferencing component also supports a one-on-one chat mode, which
is just like normal chat mode (where messages with the type
chat are exchanged) except that the routing goes through the
component. The intended recipient of a conference-routed chat message
is identified by his room JID. So in this example:

RECV: <message to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash' type='chat'> <body>Is that you, qmacro?</body> <thread>jarl1998911094</thread> </message>

the user nicknamed flash actually addressed the chat message
to the JID:

cellar@conf.merlix.dyndns.org/deejay

which arrived at the Conferencing component (because of the hostname,
conf.merlix.dyndns.org causes the <message/>
element to be routed there), which then looked up internally who
deejay really was (qmacro@jabber.com/jarltk) and
sent it on. This way, the recipient of a conference-routed message
never discovers the real JID of the sender. In all other ways, the
actual <message/> element is like any other
<message/> element—in this case, it contains a message
<body/> and a chat <thread/>. (See
Section 5.4.1 for details on the <message/> element.)
Leaving the room
In the same way that room entrance is effected by sending an
available presence (remember, a <presence/>
element without an explicit type attribute is understood to
represent type="available'), leaving a room is achieved by
doing the opposite:

RECV: <presence to='qmacro@jabber.com/jarltk' type='unavailable' from='cellar@conf.merlix.dyndns.org/roscoe'/>

The people in the conference room are sent a message that
roscoe has left the room by the unavailable presence
packet. This is by and large for the benefit of each user's client, so
that the room occupant list can be updated. The component also sends
out a verbal notification, in the same way as it sends a verbal
notification out when someone joins:

RECV: <message to='qmacro@jabber.com/jarltk' type='groupchat' from='cellar@conf.merlix.dyndns.org'> <body>roscoe has left</body> </message>

Like the join notification, the text for the leave notification ("has
left") comes directly from the component instance configuration
described in Section 4.10.3.


The Script's Scope


The Keyword Assistant (keyassist) script will be written in Python using the Jabberpy library. As mentioned earlier, the script will perform the following tasks:

  • Connect to a predetermined Jabber server
  • Join a predetermined conference room
  • Sit there quietly, listening to the conversation
  • Take simple commands from people to watch for, or stop watching for, particular words or phrases uttered in the room
  • Relay the context of those words or phrases to whomever requested them, if heard In addition

to setting the identity of the Jabber server and the conference room in variables, we'll also need to keep track of which users ask the assistant for words and phrases.
We'll use a dictionary (hash in Perl terms), as shown in Example 9-3, because we want to manage the data in there by key, the JID of those users that the script will be assisting. Having a look at what this dictionary will look like during the lifetime of this script will help us to visualize what we're trying to achieve.


Typical contents of the Keyword Assistant's dictionary

{ 'dj@gnu.pipetree.com/home': { 'http:': 1, 'ftp:': 1

                                         },
 'piers@jabber.org/work':                { 'Perl': 1, 'Java': 1, 'SAP
 R/3': 1
                                         },
 'cellar@conf.merlix.dyndns.org/roscoe': { 'dialback': 1
                                         }

}

We can see from the contents of the dictionary in Example 9-3 that three people have asked the script to look out for words and phrases. Two of those people—dj and piers—have interacted with the script directly by sending a normal (or chat) <message/>. The other person, with the conference nickname roscoe, is in the "cellar" room and has sent the script a message routed through the Conference component in the same way that flash sent qmacro a private message in Example 9-2: the JID of the sender belongs to (has the hostname set to) the conference component. Technically, there's nothing to distinguish the three JIDs here; it's just that we know from the name that conf.merlix.dyndns.org is the name that identifies such a component.

If we dissect the dictionary, we can see that:

  • dj wants to be notified if any web or FTP URLs are mentioned.
  • piers is interested in references to two of his favorite

languages and his favorite business software solution.

  • roscoe

is interested in any talk about dialback. We said we'd give the script a little bit of intelligence. This was a reference to the ability for users to interact with the script while it runs, rather than having to give the script a static list of words and phrases in a configuration file. dj, piers, and roscoe have done this by sending the script messages (directly, not within the room) with simple keyword commands, such as:

dj: "watch http:" script: "ok, watching for http:" dj:
"watch gopher:" script: "ok, watching for gopher:" dj:
"watch ftp:" script: "ok, watching for ftp:" dj: "ignore
gopher:" script: "ok, now ignoring gopher:"

...

piers: "list" script: "watching for: Perl, Java, SAP R/3"

...

roscoe: "stop" script: "ok, I've stopped watching"


The keyassist Script


Example 9-4 shows the keyassist script in its entirety. The script is described in detail, step by step, in the next section.


The keyassist Perl script

import jabber from string import split, join, find import sys

keywords = {}

def addword(jid, word): if not keywords.has_key(jid): keywords[jid] = {} keywords[jid][word] = 1

def delword(jid, word): if keywords.has_key(jid): if keywords[jid].has_key(word): del keywords[jid][word]

def messageCB(con, msg):

   type = msg.getType() if type == None: type = 'normal'
   # Deal with interaction
   if type == 'chat' or type == 'normal': jid = str(msg.getFrom())
       message = split(msg.getBody(), None, 1); reply = ""
       if message[0] == 'watch': addword(jid, message[1]) reply =
       "Okay, watching for " + message[1]
       if message[0] == 'ignore': delword(jid, message[1]) reply =
       "Okay, now ignoring " + message[1]
       if message[0] == 'list': if keywords.has_key(jid): reply =
       "Watching for: " + join(keywords[jid].keys(), ", ") else: reply
       = "Not watching for any keywords"
       if message[0] == 'stop': if keywords.has_key(jid): del
       keywords[jid] reply = "Okay, I've stopped watching"
       if reply: con.send(msg.build_reply(reply))


   # Scan room talk
   if type == 'groupchat': message = msg.getBody()
       for jid in keywords.keys(): for word in keywords[jid].keys(): if
       find(message, word) >= 0: con.send(jabber.Message(jid, word +
       ": " + message))


def presenceCB(con, prs):

   # Deal with nickname conflict in room
   if str(prs.getFrom()) == roomjid and prs.getType() == 'error':
   prsnode = prs.asNode() error = prsnode.getTag('error') if error: if
   (error.getAttr('code') == '409'): print "Cannot join room -
   conflicting nickname" con.disconnect() sys.exit(0)
   # Remove keyword list for groupchat correspondent
   if prs.getType() == 'unavailable': jid = str(prs.getFrom()) if
   keywords.has_key(jid): del keywords[jid]

Server = 'gnu.mine.nu' Username = 'kassist' Password = 'pass' Resource = 'py'

Room = 'jdev' ConfServ = 'conference.jabber.org' Nick = 'kassist'

con = jabber.Client(host=Server) try: con.connect() except IOError, e: print "Couldn't connect: %s" % e sys.exit(0) else: print "Connected"

if con.auth(Username,Password,Resource): print "Logged in as %s to server %s" % ( Username, Server ) else: print "Problems authenticating: ", con.lastErr, con.lastErrCode sys.exit(1)

con.setMessageHandler(messageCB) con.setPresenceHandler(presenceCB)

con.send(jabber.Presence())

roomjid = Room + '@' + ConfServ + '/' + Nick print "Joining " + Room con.send(jabber.Presence(to=roomjid))

while(1): con.process(5)


Dissecting the keyassist Script


Taking keyassist step by step, the first section is probably familiar if you've seen the previous Python-based scripts in Section 8.1 and Section 8.3, both in Chapter 8.


import jabber from string import split, join, find import sys

Here, all of the functions and libraries that we'll need are brought in. We'll use the find function from the string library to help with the keyword searching.

Next, we declare the dictionary. This will hold a list of the words that the script will look for, as defined by each person, as shown in Example 9-3.


keywords = {}


Maintaining the keyword dictionary


To maintain this dictionary, we will use two subroutines to add words to and remove words from a user's word list. These subroutines are called when a command such as watch or ignore is recognized in the callback subroutine that handles incoming <message/> elements:


def addword(jid, word): if not keywords.has_key(jid): keywords[jid] = {} keywords[jid][word] = 1

def delword(jid, word): if keywords.has_key(jid): if keywords[jid].has_key(word): del keywords[jid][word]

A string representation of the JID (in jid) of the correspondent giving the command is passed to the subroutines along with the word or phrase specified (in word) by the user. The dictionary has two levels: the first level is keyed by the JID, and the second by word or phrase. We use a dictionary, rather than an array, at the second level simply to make removal of words and phrases easier.



Message callback


Next, we define the callback to handle incoming <message/> elements:


def messageCB(con, msg):

   type = msg.getType() if type == None: type = 'normal'

As usual, we're expecting the message callback to be passed the connection object (in con) and the message object itself (msg). How this callback is to proceed is determined by the type of message received. We determine the type (taken from the <message/> element's type attribute) and store it in the variable called type. Remember that if no type attribute is present, a message type of normal is assumed. (See Section 5.4.1.1 for details of <message/> attributes.)

The two types of incoming messages we're expecting this script to receive are those conveying the room's conversation—in groupchat-type messages—and those over which the commands such as watch and ignore are carried, which we expect in the form of normal- or chat-type messages.

The first main section of the messageCB handler deals with incoming commands:


# Deal with interaction if type == 'chat' or type == 'normal': jid = str(msg.getFrom())

       message = split(msg.getBody(), None, 1); reply = ""
       if message[0] == 'watch': addword(jid, message[1]) reply =
       "Okay, watching for " + message[1]
       if message[0] == 'ignore': delword(jid, message[1]) reply =
       "Okay, now ignoring " + message[1]
       if message[0] == 'list': if keywords.has_key(jid): reply =
       "Watching for: " + join(keywords[jid].keys(), ", ") else: reply
       = "Not watching for any keywords"
       if message[0] == 'stop': if keywords.has_key(jid): del
       keywords[jid] reply = "Okay, I've stopped watching"
       if reply: con.send(msg.build_reply(reply))


If the <message/> element turns out to be of the type in which we're expecting a potential command, we want to determine the JID of the correspondent who sent that message. Calling the getFrom() method will return us a JID object. What we need is the string representation of that, which can be determined by calling the str() function on that JID object:


jid = str(msg.getFrom())

Then we grab the content of the message by calling the getBody() on the msg object and split the whole thing on the first bit of whitespace. This should be enough for us to distinguish a command (watch, ignore, and so on) from the keywords. After the split, the first element (index 0) in the message array will be the command, and the second element (index 1) will be the word or phrase, if given. At this stage, we also declare an empty reply:


message = split(msg.getBody(), None, 1); reply = ""

Now it's time to determine if what the script was sent made sense as a command:


if message[0] == 'watch': addword(jid, message[1]) reply = "Okay, watching for " + message[1]

       if message[0] == 'ignore': delword(jid, message[1]) reply =
       "Okay, now ignoring " + message[1]
       if message[0] == 'list': if keywords.has_key(jid): reply =
       "Watching for: " + join(keywords[jid].keys(), ", ") else: reply
       = "Not watching for any keywords"
       if message[0] == 'stop': if keywords.has_key(jid): del
       keywords[jid] reply = "Okay, I've stopped watching"

We go through a series of checks, taking appropriate action for the supported commands:


watch
Watch for a particular word or phrase.
ignore
Stop watching for a particular word or phrase.
list
List the words and phrases currently being watched.
stop
Stop watching altogether; remove the list of words and phrases. The
addword() and delword() functions defined earlier
are used here, as well as other simpler functions; one that lists the
words and phrases for a particular JID:


keywords[jid].keys()

and one that removes them:


del keywords[jid]

If there was something recognizable for the script to do, we get it to reply appropriately:


if reply: con.send(msg.build_reply(reply))

The build_reply() function creates a reply out of a message object by setting to to the value of the original <message/> element's from attribute and preserving the element type attribute and <thread/> tag, if present. The <body/> of the reply object (which is just a <message/> element) is set to whatever is passed in the function call; in this case, it's the text in the reply variable.

Now that we've dealt with incoming commands, we need another section in the message callback subroutine to scan for the words and phrases. The target texts for this scanning will be the snippets of room conversation, which arrive at the callback in the form of groupchat-type <message/> elements:


# scan room talk if type == 'groupchat': message = msg.getBody()

The message variable holds the string we need to scan; it's just a case of checking for each of the words or phrases on behalf of each of the users who have asked:


for jid in keywords.keys(): for word in keywords[jid].keys(): if find(message, word) >= 0: con.send(jabber.Message(jid, word + ": " + message))

If we get a hit, we construct a new Message object, passing the JID of the person for whom the string has matched (in the jid variable) and the notification, consisting of the word or phrase that was found (in word) and the context in which it was found (the sentence uttered, in message). Once found and constructed, the <message/> is sent to that user. By default, the Message constructor specifies no type attribute, so the user is sent a "normal" message.


Presence callback


Having dealt with the incoming <message/> elements, we turn to the <presence/> elements. Most of those we receive in this conference room will be notifications from people entering and leaving the room, as shown in Example 9-2. We want to perform housekeeping on our keywords dictionary so the entries don't become stale. We also want to deal with the potential problem of conflicting nicknames. Let's look at that first.

We want to check for the possibility of nickname conflict problems that may occur when we enter the room, and the chosen nickname (flash) is already taken.

Remembering that a conflict notification will look something like this:


<presence to='qmacro@jabber.com/jarltk' from='cellar@conf.merlix.dyndns.org/flash' type='error'> <error code='409'>Conflict</error> </presence> we test for the receipt of a <presence/> element with the following:


def presenceCB(con, prs):

   # Deal with nickname conflict in room
   if str(prs.getFrom()) == roomjid and prs.getType() == 'error':
   prsnode = prs.asNode() error = prsnode.getTag('error') if error: if
   (error.getAttr('code') == '409'): print "Cannot join room -
   conflicting nickname" con.disconnect() sys.exit(0)

The <presence/> element will appear to be sent from the JID that we constructed for the initial room entry negotiation (in the roomjid variable further down in the script); for example:


jdev@conference.jabber.org/kassist We compare this value to the value of the incoming <presence/>'s from attribute, and also make sure that the type attribute is set to error. If it is, we want to extract the details from the <error/> tag that will be contained as a direct child of the <presence/>.

The Jabberpy library currently doesn't offer a direct high-level function to get at this tag from the Presence object (in prs), but we can strip away the presence object "mantle" and get at the underlying object, which is a neutral "node"—a Jabber element, or XML fragment, without any preconceived ideas of what it is (and therefore without any accompanying high-level methods such as getBody() or setPriority()).


{{Note|If this seems a little cryptic, just think of it like this: each of the Presence, Message, and IQ classes are merely superclasses of the base class Protocol, which represents elements generically.

</code> The asNode() method gives us what we need—a Protocol object representation of the <presence/> element. From this we can get to the <error/> tag and its contents. If we find that we do have a nickname conflict, we abort by disconnecting from the Jabber server and ending the script.

The general idea is that this script will run indefinitely and notify the users on a continuous basis, so we need to do a spot of keyword housekeeping. No presence subscription relationships are built (mostly to keep the script small and simple; you could adapt the mechanism from the recipe in Section 8.3 if you wanted to make this script sensitive to presence), so notifications will get queued up for the user if he is offline with the use of the mod_offline module of the Jabber Session Manager (JSM). This makes a lot of sense for the most part; however, we still want to have the script send notifications even if the user is offline. Additionally, a command could be sent to the script to watch for a keyword or phrase from a user within the room. We would receive the command from a JID like this:


jdev@conference.jabber.org/nickname This is a transient JID, in that it represents a user's presence in the jdev room for a particular session. If a word is spotted by the script hours or days later, there's a good chance that the user has left the room, making the JID invalid as a recipient. Although the JID is technically valid and will reach the conferencing component, there will be no real user JID that it is paired up with. Potentially worse, the room occupant's identity JID may be assigned to someone else at a later stage, if the original user left, and a new user entered choosing the same nickname the original user had chosen. See the upcoming sidebar titled "Transient and Nonexistent JIDs" for a short discussion of the difference between a transient JID and a nonexistent JID.

So as soon as we notice a user leave the room we're in, which will be indicated through a <presence/> element conveying that occupant's unavailability, we should remove any watched-for words and phrases from the dictionary:


# Remove keyword list for groupchat correspondent if prs.getType() == 'unavailable': jid = str(prs.getFrom()) if keywords.has_key(jid): del keywords[jid]

As before, we obtain the string representation of the JID using the str() function on the JID object that represents the presence element's sender, obtained via the getFrom() method.


{{Sidebar|Transient and Nonexistent JIDsWhat happens when you send a message to a "transient" conference room JID? Superficially, the same as when you send one to a nonexistent JID. But there are some subtle differences.

A transient JID is one that reflects a user's alternate identity in the context of the Conferencing component. When you construct and send a message to a conference transient JID, it goes first to the conference component because of the hostname in the JID that identifies that component, for example:


jdev@conference.jabber.org/qmacro The hostname conference.jabber.org is what the jabberd backbone uses to route the element. As mentioned earlier, the Conferencing component will relay a message to the real JID that belongs to the user currently in a room hosted by that component.

While the component itself is usually persistent, the room occupants (and so their transient JIDs) are not. When a message is sent to the JID jdev@conference.jabber.org/qmacro and there is no room occupant in the jdev room with the nickname qmacro, the message will still reach its first destination—the component—but be rejected at that stage, as shown in Example 9-5.


A message to a nonexistent transient JID is rejected

SEND: <message to='jdev@conference.jabber.org/qmacro'> <body>Hello there</body> </message>

RECV: <message to='dj@gnu.mine.nu/jarl' from='jdev@conference.jabber.org/qmacro' type='error'> <body>Hello there</body> <error code='404'>Not Found</error> </message>

Although the rejection—the "Not Found" error—is the same as if a message had been sent to a JSM user that didn't exist, the difference is that the transient user always had the potential to exist, whereas the JSM user never did. Of course, if the JID referred to a nonexistent Jabber server, then the error returned wouldn't be a "Not Found" error 404, but an "Unable to resolve hostname" error 502.

</code>

The main script


Now that we have the subroutines and callbacks set up, all we need to do is define the Jabber server and room information:


Server = 'gnu.mine.nu' Username = 'kassist' Password = 'pass' Resource = 'py'

Room = 'jdev' ConfServ = 'conference.jabber.org' Nick = 'kassist'

The kassist user can be set up simply by using the reguser script presented in Section 7.4:


$ ./reguser gnu.mine.nu username=kassist password=pass [Attempt] (kassist) Successful registration $ In the same way as in previous recipes' scripts, a connection attempt is made, followed by an authentication attempt:


con = jabber.Client(host=Server,debug=0,log=0) try: con.connect() except IOError, e: print "Couldn't connect: %s" % e sys.exit(0) else: print "Connected"

if con.auth(Username,Password,Resource): print "Logged in as %s to server %s" % ( Username, Server ) else: print "Problems authenticating: ", con.lastErr, con.lastErrCode sys.exit(1)

Then the message and presence callbacks messageCB() and presenceCB() are defined to the connection object in con:


con.setMessageHandler(messageCB) con.setPresenceHandler(presenceCB)

After sending initial presence, informing the JSM (and anyone who might be subscribed to kassist's presence) of the assistant's availability:


con.send(jabber.Presence())

we also construct—from the Room, ConfServ, and Nick variables—and send the <presence/> element for negotiating entry to the jdev room hosted by the Conferencing component at conference.jabber.org:


roomjid = Room + '@' + ConfServ + '/' + Nick print "Joining " + Room con.send(jabber.Presence(to=roomjid))

The con.send() function will send a <presence/> element that looks like this:


SEND: <presence to='jdev@conference.jabber.org/kassist'/> We're sending available presence to the room, to negotiate entry, but what about the initial presence? Why do we send that too if there are no users who will be subscribed to the kassist JID? If no initial presence is sent, the JSM will merely store up any <message/> elements destined for kassist, as it will think the JID is offline.


The processing loop


Once everything has been set up, we simply need to have the script sit back and wait for incoming packets and handle them appropriately. For this, we simply call the process() function every 5 seconds to look for elements arriving on the XML stream:


while(1): con.process(5)


Connecting Devices to Jabber


LEGO MINDSTORMS. What a great reason to dig out that box of LEGO bricks you haven't touched in years. When I found out that LEGO was bringing out a programmable brick, the RCX,[2], I went to my favorite toy shop and purchased the set. In addition to the RCX (shown in Figure 9-1), the MINDSTORMS set comes with an infrared (IR) port and an IR tower, which you can connect to the serial port of your PC, a battery compartment,[3] motors, touch and light sensors, and various LEGO Technic parts.

The LEGO MINDSTORMS RCX, or "programmable brick"


</code> There are plenty of ways to interact with the RCX. The MINDSTORMS Robotics Invention System (RIS)™ set comes with Windows software with which you can build programs by moving blocks of logic around graphically on the screen and chaining them together. In addition, various efforts on the parts of talented individuals have come up with many different ways to program the RCX. The Unofficial Guide to LEGO® MINDSTORMSâÃÃÂ�

Personal tools