⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 x8004.htm

📁 Its a xmpp protocol book
💻 HTM
📖 第 1 页 / 共 4 页
字号:
        if message[0] == 'watch':
            addword(jid, message[1])
            reply = "ok, watching for " + message[1]
        
        if message[0] == 'ignore':
            delword(jid, message[1])
            reply = "ok, 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 = "ok, I've stopped watching"
           
        if reply:
            con.send(msg.build_reply(reply))
&#13;</PRE
></P
><P
>Here's that section one chunk at a time.</P
><P
>If the <TT
CLASS="LITERAL"
>&#60;message/&#62;</TT
> 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 <TT
CLASS="FUNCTION"
>getFrom()</TT
> method will return us a JID
object. What we need is the string representation of that, which can
be determined by calling the <TT
CLASS="FUNCTION"
>str()</TT
> function on
that JID object:</P
><P
><PRE
CLASS="SCREEN"
>        jid = str(msg.getFrom())&#13;</PRE
></P
><P
>Then we grab the content of the message by calling the 
<TT
CLASS="FUNCTION"
>getBody()</TT
> on the <TT
CLASS="LITERAL"
>msg</TT
> 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 
<TT
CLASS="LITERAL"
>message</TT
> 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.</P
><P
><PRE
CLASS="SCREEN"
>        message = split(msg.getBody(), None, 1);
        reply = ""&#13;</PRE
></P
><P
>Now it's time to determine whether what the script was sent made sense
as a command:</P
><P
><PRE
CLASS="SCREEN"
>        if message[0] == 'watch':
            addword(jid, message[1])
            reply = "ok, watching for " + message[1]
        
        if message[0] == 'ignore':
            delword(jid, message[1])
            reply = "ok, 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 = "ok, I've stopped watching"&#13;</PRE
></P
><P
>We go through a series of checks, taking appropriate action for our
supported commands:</P
><P
></P
><UL
><LI
><P
><TT
CLASS="LITERAL"
>watch</TT
> (watch for a particular word
or phrase)</P
></LI
><LI
><P
><TT
CLASS="LITERAL"
>ignore</TT
> (stop watching for a particular
word or phrase)</P
></LI
><LI
><P
><TT
CLASS="LITERAL"
>list</TT
> (list the words and phrases 
currently being watched)</P
></LI
><LI
><P
><TT
CLASS="LITERAL"
>stop</TT
> (stop watching
altogether&mdash;remove my list of words and phrases)</P
></LI
></UL
><P
>The <TT
CLASS="FUNCTION"
>addword()</TT
> and <TT
CLASS="FUNCTION"
>delword()</TT
>
functions defined earlier are used here, as well as other simpler 
functions that list:</P
><P
><PRE
CLASS="SCREEN"
>keywords[jid].keys()&#13;</PRE
></P
><P
>or remove:</P
><P
><PRE
CLASS="SCREEN"
>del keywords[jid]&#13;</PRE
></P
><P
>the words and phrases for a particular JID.</P
><P
>If there was something recognisable for the script to do, we get it to reply
appropriately:</P
><P
><PRE
CLASS="SCREEN"
>        if reply:
            con.send(msg.build_reply(reply))&#13;</PRE
></P
><P
>The <TT
CLASS="FUNCTION"
>build_reply()</TT
> function creates a reply out of
a message object by setting the <TT
CLASS="LITERAL"
>to</TT
> to the value of
the original <TT
CLASS="LITERAL"
>&#60;message/&#62;</TT
> element's 
<TT
CLASS="LITERAL"
>from</TT
> attribute, and preserving the element 
<TT
CLASS="LITERAL"
>type</TT
> attribute and <TT
CLASS="LITERAL"
>&#60;thread/&#62;</TT
>
tag, if present. The <TT
CLASS="LITERAL"
>&#60;body/&#62;</TT
> of the 
reply object (which is, after all, just a
<TT
CLASS="LITERAL"
>&#60;message/&#62;</TT
> element), is set to whatever
is passed in the function call; in this case, it's the text in the
<TT
CLASS="LITERAL"
>reply</TT
> variable.</P
></DD
><DT
>Word and phrase scanning</DT
><DD
><P
>Now that we've dealt with incoming commands, we just 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 "<I
CLASS="EMPHASIS"
>groupchat</I
>"
type <TT
CLASS="LITERAL"
>&#60;message/&#62;</TT
> elements.</P
><P
><PRE
CLASS="SCREEN"
>    # scan room talk
    if type == 'groupchat':
        message = msg.getBody()&#13;</PRE
></P
><P
>The <TT
CLASS="LITERAL"
>message</TT
> variable holds the string we need to scan,
and it's just a case of checking for each of the words or phrases on 
behalf of each of the users that have asked:</P
><P
><PRE
CLASS="SCREEN"
>        for jid in keywords.keys():
            for word in keywords[jid].keys():
                if find(message, word) &#62;= 0:
                    con.send(Jabber.Message(jid, word + ": " + message))&#13;</PRE
></P
><P
>If we get a hit, we construct a new <TT
CLASS="LITERAL"
>Message</TT
> object,
passing the JID
of the person for whom the string has matched (in the <TT
CLASS="LITERAL"
>jid</TT
>
variable), and the notification consisting of the word or phrase that was
found (in <TT
CLASS="LITERAL"
>word</TT
>) and the context in which it was found
(the sentence uttered, in <TT
CLASS="LITERAL"
>message</TT
>).
The <TT
CLASS="LITERAL"
>&#60;message/&#62;</TT
> so constructed, is then
sent to that user. By default, the <TT
CLASS="LITERAL"
>Message</TT
> constructor
specifies no <TT
CLASS="LITERAL"
>type</TT
> attribute, so that the user is sent
a "normal" message. </P
></DD
></DL
></DIV
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-1.3.3"
>Presence callback</A
></H3
><P
>Having dealt with the incoming <TT
CLASS="LITERAL"
>&#60;message/&#62;</TT
>
elements that we're expecting, we turn our attention to 
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> elements. Most of those we
receive in this conference room context will be notifications of people
entering and leaving the room that we're going to be in, as shown in 
<A
HREF="x8004.htm#JABTDG-CH-8-EX-2"
>Example 8-2</A
>. We want to perform housekeeping on
our <TT
CLASS="LITERAL"
>keywords</TT
> hash so that the entries don't become
stale. We also want to deal with the potential nickname conflict problem.</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><DL
><DT
>Nickname conflict</DT
><DD
><P
>We want to check for the possibility of nickname conflict 
problems which may occur when we enter the room, and our chosen 
nickname is already taken. </P
><P
>Remembering that a conflict notification will look something like this:</P
><P
><PRE
CLASS="SCREEN"
>&#60;presence to='qmacro@jabber.com/jarltk'
        from='cellar@conf.merlix.dyndns.org/flash' 
        type='error'&#62;
  &#60;error code='409'&#62;Conflict&#60;/error&#62;
&#60;/presence&#62;</PRE
></P
><P
>we test for the receipt of a <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>
element so formed:</P
><P
><PRE
CLASS="SCREEN"
>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)&#13;</PRE
></P
><P
>The <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element will appear to 
be sent from the JID that we constructed for our initial room entry
negotiation (in the <TT
CLASS="LITERAL"
>roomjid</TT
> variable further down
in the script), for example, in our case:</P
><P
><PRE
CLASS="SCREEN"
>jdev@conference.jabber.org/kassist</PRE
></P
><P
>We compare this value to the value of the incoming 
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>'s <TT
CLASS="LITERAL"
>from</TT
>
attribute, and also make sure that the <TT
CLASS="LITERAL"
>type</TT
> attribute
is set to "<TT
CLASS="LITERAL"
>error</TT
>".</P
><P
>If it is, we want to extract the
details from the <TT
CLASS="LITERAL"
>&#60;error/&#62;</TT
> tag that will
be contained as a direct child of the 
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>. The JabberPy library doesn't
offer a direct high-level function to get at this tag from the 
<TT
CLASS="LITERAL"
>Presence</TT
> object (in <TT
CLASS="LITERAL"
>prs</TT
>), but
we can strip away the presence object "mantle" and get at the underlying
object, which is a neutral "node"&mdash;a Jabber element, or XML fragment,
without any pre-conceived ideas of what it is (and therefore without any
accompanying high-level methods such as <TT
CLASS="FUNCTION"
>getBody()</TT
>
or <TT
CLASS="FUNCTION"
>setPriority()</TT
>.
<A
NAME="AEN8446"
HREF="#FTN.AEN8446"
>[6]</A
></P
><P
>The <TT
CLASS="FUNCTION"
>asNode()</TT
> method gives us what we need - a 
<TT
CLASS="LITERAL"
>Protocol</TT
> object representation of our 
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element. From this we can 
get to the <TT
CLASS="LITERAL"
>&#60;error/&#62;</TT
> 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.</P
></DD
><DT
>Keyword housekeeping</DT
><DD
><P
>The general idea is that this script will run indefinitely and notify
the users on a continuous basis. No presence subscription relationships
are built (mostly to keep the script small and simple; you could adapt
the mechanism from the recipe in 
<A
HREF="x7499.htm"
>the section called <I
>Presence-sensitive CVS notification</I
> in Chapter 7</A
> if you wanted to make this script
sensitive to presence) and so notifications will get queued up for the
user if he is offline.
<A
NAME="AEN8462"
HREF="#FTN.AEN8462"
>[7]</A
>
This makes a lot of sense for the most part; I still want to have the
script send me notifications even if I'm offline. However, consider
that the
script could be sent a command, to watch for a keyword or phrase, from
a user within the room. We would receive the command from a JID like
this:</P
><P
><PRE
CLASS="SCREEN"
>jdev@conference.jabber.org/nickname</PRE
></P
><P
>This is a "transient" JID, in that it represents a user's presence in the
<I
CLASS="EMPHASIS"
>jdev</I
> 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&mdash;although the JID is <I
CLASS="EMPHASIS"
>technically</I
> 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 nick as the original user had chosen. 
<A
HREF="x8004.htm#JABTDG-CH-8-SIDE-1"
>the sidebar <I
>Transient JIDs and non-existent JIDs</I
></A
> discusses the difference between a
"transient" JID and a non-existent JID.</P
><P
>So as soon as we notice a user leave the room we're in, which will be
indicated through a <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element
conveying that occupant's <I
CLASS="EMPHASIS"
>unavailability</I
>, we 
should remove any watched-for words and phrases from our hash:</P
><P
><PRE
CLASS="SCREEN"
>    # remove keyword list for groupchat correspondent
    if prs.getType() == 'unavailable':
        jid = str(prs.getFrom())
        if keywords.has_key(jid):
            del keywords[jid]&#13;</PRE
></P
><P
>As before, we obtain the string representation of the JID using the
<TT
CLASS="FUNCTION"
>str()</TT
> function on the JID object that represents
the presence element's <I
CLASS="EMPHASIS"
>sender</I
>, obtained via the
<TT
CLASS="LITERAL"
>getFrom()</TT
> method.</P
></DD
></DL
></DIV
><TABLE
CLASS="SIDEBAR"
BORDER="1"
CELLPADDING="5"
><TR
><TD
><DIV
CLASS="SIDEBAR"
><A
NAME="JABTDG-CH-8-SIDE-1"
></A
><P
><B
>Transient JIDs and non-existent JIDs</B
></P
><P
>What happens when you send a message to a conference room "transient" JID?
Superficially, the same as when you send one to a
<I
CLASS="EMPHASIS"
>non-existent</I
> JID. But there are some subtle differences.</P
><P
>A transient JID is one that reflects a user's alternative identity in the 
context of a component. In this case, the component is the 
<I
CLASS="EMPHASIS"
>Conferencing</I
> 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 identifies that component, 
for example:</P
><P
><PRE
CLASS="SCREEN"
>jdev@conference.jabber.org/qmacro</PRE
></P
><P
>The hostname <TT
CLASS="LITERAL"
>conference.jabber.org</TT
> is what the
<B
CLASS="COMMAND"
>jabberd</B
> backbone uses to route the element. As 
mentioned earlier, the <I
CLASS="EMPHASIS"
>Conferencing</I
> component
will relay a message to the real JID that belongs to the user that
is currently in a room hosted by that component. </P
><P
>While the component itself is usually persistent, the room occupants
(and so their transient JIDs) are not. This means that when a message
is sent to the JID <TT
CLASS="LITERAL"
>jdev@conference.jabber.org/qmacro</TT
>
and there is no room occupant in the "jdev" room with the nickname "qmacro",
the message will still reach its <I
CLASS="EMPHASIS"
>first</I
>
destination&mdash;the 
component&mdash;but be rejected at that stage, as shown in 
<A
HREF="x8004.htm#JABTDG-CH-8-EX-4"
>Example 8-4</A
>. </P
><P
>Although the rejection&mdash;the "Not Found" error&mdash; 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
<I
CLASS="EMPHASIS"
>potential</I
> to exist, whereas the JSM user never did.
<A
NAME="AEN8498"
HREF="#FTN.AEN8498"
>[8]</A
></P
></DIV
></TD
></TR
></TABLE
><DIV
CLASS="EXAMPLE"
><A
NAME="JABTDG-CH-8-EX-4"
></A
><P
><B
>Example 8-4. A message to a non-existent transient JID is rejected</B
></P
><P
><PRE

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -