source: wokkel/muc.py @ 118:a70736af32c3

wokkel-muc-client-support-24
Last change on this file since 118:a70736af32c3 was 118:a70736af32c3, checked in by Christopher Zorn <tofu@…>, 14 years ago

add timeouts and a test for a user leaving a room

File size: 23.5 KB
RevLine 
[107]1# -*- test-case-name: wokkel.test.test_muc -*-
2#
3# Copyright (c) 2003-2008 Ralph Meijer
4# See LICENSE for details.
5
6"""
7XMPP Multi-User Chat protocol.
8
9This protocol is specified in
10U{XEP-0045<http://www.xmpp.org/extensions/xep-0045.html>}.
11"""
[110]12import datetime
[107]13
14from zope.interface import implements
15
[118]16from twisted.internet import defer, reactor
[107]17from twisted.words.protocols.jabber import jid, error, xmlstream
18from twisted.words.xish import domish
19
20from wokkel import disco, data_form, shim, xmppim
21from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
[108]22from wokkel.iwokkel import IMUCClient
[107]23
24# Multi User Chat namespaces
[116]25NS_MUC          = 'http://jabber.org/protocol/muc'
26NS_MUC_USER     = NS_MUC + '#user'
27NS_MUC_ADMIN    = NS_MUC + '#admin'
28NS_MUC_OWNER    = NS_MUC + '#owner'
29NS_MUC_ROOMINFO = NS_MUC + '#roominfo'
30NS_MUC_CONFIG   = NS_MUC + '#roomconfig'
31NS_MUC_REQUEST  = NS_MUC + '#request'
32NS_MUC_REGISTER = NS_MUC + '#register'
[110]33
[116]34NS_DELAY        = 'urn:xmpp:delay'
35NS_JABBER_DELAY = 'jabber:x:delay'
36
[111]37NS_REQUEST  = 'jabber:iq:register'
[107]38
39# ad hoc commands
40NS_AD_HOC       = "http://jabber.org/protocol/commands"
41
42
43# Iq get and set XPath queries
44IQ     = '/iq'
45IQ_GET = IQ+'[@type="get"]'
46IQ_SET = IQ+'[@type="set"]'
47
48IQ_RESULT = IQ+'[@type="result"]'
49IQ_ERROR  = IQ+'[@type="error"]'
50
51IQ_QUERY     = IQ+'/query'
52IQ_GET_QUERY = IQ_GET + '/query'
53IQ_SET_QUERY = IQ_SET + '/query'
54
55IQ_COMMAND   = IQ+'/command'
56
[116]57MUC_ADMIN = IQ_QUERY+'[@xmlns="' + NS_MUC_ADMIN + '"]'
58MUC_OWNER = IQ_QUERY+'[@xmlns="' + NS_MUC_OWNER + '"]'
[107]59
60MUC_AO = MUC_ADMIN + '|' + MUC_OWNER
61
62
63MESSAGE  = '/message'
64PRESENCE = '/presence'
65
66CHAT_BODY = MESSAGE +'[@type="chat"]/body'
67CHAT      = MESSAGE +'[@type="chat"]'
68
[110]69GROUPCHAT     = MESSAGE +'[@type="groupchat"]/body'
[113]70SUBJECT       = MESSAGE +'[@type="groupchat"]/subject'
[107]71MESSAGE_ERROR = MESSAGE +'[@type="error"]'
72
[110]73STATUS_CODES = { # see http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes
74    100:
75        {'name':'fulljid',
76         'stanza':'presence',
77         
78         },
79    201: 
80        {'name':'created', 
81         'stanza': 'presence',
82         'context':'Entering a room',
83         'purpose':'Inform user that a new room has been created'
84         },   
85}
86
87STATUS_CODE_CREATED = 201
[107]88
[118]89DEFER_TIMEOUT = 30 # basic timeout is 30 seconds
[107]90
91class MUCError(error.StanzaError):
92    """
93    Exception with muc specific condition.
94    """
95    def __init__(self, condition, mucCondition, feature=None, text=None):
[116]96        appCondition = domish.Element((NS_MUC, mucCondition))
[107]97        if feature:
98            appCondition['feature'] = feature
99        error.StanzaError.__init__(self, condition,
100                                         text=text,
101                                         appCondition=appCondition)
102
103
104class BadRequest(MUCError):
105    """
106    Bad request stanza error.
107    """
108    def __init__(self, mucCondition=None, text=None):
109        MUCError.__init__(self, 'bad-request', mucCondition, text)
110
111
112
113class Unsupported(MUCError):
114    def __init__(self, feature, text=None):
115        MUCError.__init__(self, 'feature-not-implemented',
116                          'unsupported',
117                          feature,
118                          text)
119
120
[108]121
[110]122class ConfigureRequest(xmlstream.IQ):
123    """
124    Configure MUC room request.
125
[113]126    http://xmpp.org/extensions/xep-0045.html#roomconfig
127
[110]128    @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
129    @type method: C{str}
130    """
131
132    def __init__(self, xs, method='get', fields=[]):
133        xmlstream.IQ.__init__(self, xs, method)
[116]134        q = self.addElement((NS_MUC_OWNER, 'query'))
[110]135        if method == 'set':
136            # build data form
[116]137            form = data_form.Form('submit', formNamespace=NS_MUC_CONFIG)
[110]138            q.addChild(form.toElement())
139           
140            for f in fields:
141                # create a field
142                form.addField(f)
143
144
[111]145class RegisterRequest(xmlstream.IQ):
146    """
147    Register room request.
148
149    @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
150    @type method: C{str}
[112]151
[111]152    """
153
154    def __init__(self, xs, method='get', fields=[]):
155        xmlstream.IQ.__init__(self, xs, method)
156        q = self.addElement((NS_REQUEST, 'query'))
157        if method == 'set':
158            # build data form
159            form_type = 'submit'       
[116]160            form = data_form.Form(form_type, formNamespace=NS_MUC_REGISTER)
[111]161            q.addChild(form.toElement())       
162           
163            for f in fields:
164                # create a field
165                form.addField(f)
166
[112]167
168class AffiliationRequest(xmlstream.IQ):
169    """
170    Register room request.
171
172    @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
173    @type method: C{str}
174
175    @ivar affiliation: The affiliation type to send to room.
176    @type affiliation: C{str}
177
178    """
179
180    def __init__(self, xs, method='get', affiliation='none', a_jid=None, reason=None):
181        xmlstream.IQ.__init__(self, xs, method)
182       
[116]183        q = self.addElement((NS_MUC_ADMIN, 'query'))
[112]184        i = q.addElement('item')
185
186        i['affiliation'] = affiliation
187        if a_jid:
188            i['jid'] = a_jid.full()
189           
190        if reason:
191            i.addElement('reason', None, reason)
192
193           
194       
195
[110]196class GroupChat(domish.Element):
197    """
198    """
199    def __init__(self, to, body=None, subject=None, frm=None):
200        """To needs to be a string
201        """
202        domish.Element.__init__(self, (None, 'message'))
203        self['type'] = 'groupchat'
[116]204        if isinstance(to, jid.JID):
205            self['to'] = to.userhost()
206        else:
207            self['to'] = to
[110]208        if frm:
209            self['from'] = frm
210        if body:
211            self.addElement('body',None, body)
212        if subject:
213            self.addElement('subject',None, subject)
[112]214
215
216class PrivateChat(domish.Element):
217    """
218    """
219    def __init__(self, to, body=None, frm=None):
220        """To needs to be a string
221        """
222        domish.Element.__init__(self, (None, 'message'))
223        self['type'] = 'chat'
224        self['to']   = to
225        if frm:
226            self['from'] = frm
227        if body:
228            self.addElement('body',None, body)
[110]229           
[112]230class InviteMessage(PrivateChat):
231    def __init__(self, to, reason=None, full_jid=None, body=None, frm=None, password=None):
232        PrivateChat.__init__(self, to, body=body, frm=frm)
233        del self['type'] # remove type
[116]234        x = self.addElement('x', NS_MUC_USER)
[112]235        invite = x.addElement('invite')
236        if full_jid:
237            invite['to'] = full_jid
238        if reason:
239            invite.addElement('reason', None, reason)
240        if password:
241            invite.addElement('password', None, password)
[110]242
[112]243class HistoryMessage(GroupChat):
244    """
245    """
246    def __init__(self, to, stamp, body=None, subject=None, frm=None, h_frm=None):
247        GroupChat.__init__(self, to, body=body, subject=subject, frm=frm)
248        d = self.addElement('delay', NS_DELAY)
249        d['stamp'] = stamp
250        if h_frm:
251            d['from'] = h_frm
[110]252
[113]253class User(object):
254    """
255    A user/entity in a multi-user chat room.
256    """
257   
258    def __init__(self, nick, user_jid=None):
259        self.nick = nick
260        self.user_jid = user_jid
261        self.affiliation = 'none'
262        self.role = 'none'
263       
264        self.status = None
265        self.show   = None
266
267
[107]268class Room(object):
269    """
270    A Multi User Chat Room
271    """
272
273   
274    def __init__(self, name, server, nick, state=None):
275        """
276        """
277        self.state  = state
278        self.name   = name
279        self.server = server
280        self.nick   = nick
[114]281        self.status = 0
[110]282
[117]283        self.entity_id = self.entityId()
[107]284               
285        self.roster = {}
286
[117]287    def entityId(self):
288        """
289        """
290        self.entity_id = jid.internJID(self.name+'@'+self.server+'/'+self.nick)
291
292        return self.entity_id
[107]293
[113]294    def addUser(self, user):
295        """
296        """
297        self.roster[user.nick.lower()] = user
298
299    def inRoster(self, user):
300        """
301        """
302
303        return self.roster.has_key(user.nick.lower())
304
305    def getUser(self, nick):
306        """
307        """
308        return self.roster.get(nick.lower())
309
[114]310    def removeUser(self, user):
311        if self.inRoster(user):
312            del self.roster[user.nick.lower()]
[113]313       
314
[111]315class BasicPresence(xmppim.AvailablePresence):
[107]316    """
317    This behaves like an object providing L{domish.IElement}.
318
319    """
320
[111]321    def __init__(self, to=None, show=None, statuses=None):
322        xmppim.AvailablePresence.__init__(self, to=to, show=show, statuses=statuses)
[107]323        # add muc elements
[116]324        x = self.addElement('x', NS_MUC)
[107]325
326
327class UserPresence(xmppim.Presence):
328    """
329    This behaves like an object providing L{domish.IElement}.
330
331    """
332
333    def __init__(self, to=None, type=None, frm=None, affiliation=None, role=None):
334        xmppim.Presence.__init__(self, to, type)
335        if frm:
336            self['from'] = frm
337        # add muc elements
[116]338        x = self.addElement('x', NS_MUC_USER)
[107]339        if affiliation:
340            x['affiliation'] = affiliation
341        if role:
342            x['role'] = role
343
[118]344class UnavailableUserPresence(xmppim.UnavailablePresence):
345    """
346    This behaves like an object providing L{domish.IElement}.
347
348    """
349
350    def __init__(self, to=None, type=None, frm=None, affiliation=None, role=None):
351        xmppim.UnavailablePresence.__init__(self, to, type)
352        if frm:
353            self['from'] = frm
354        # add muc elements
355        x = self.addElement('x', NS_MUC_USER)
356        if affiliation:
357            x['affiliation'] = affiliation
358        if role:
359            x['role'] = role
360
[107]361
[110]362class PasswordPresence(BasicPresence):
363    """
364    """
365    def __init__(self, to, password):
366        BasicPresence.__init__(self, to)
367       
368        self.x.addElement('password', None, password)
369
370
371class MessageVoice(GroupChat):
372    """
373    """
374    def __init__(self, to=None, frm=None):
375        GroupChat.__init__(self, to=to, frm=frm)
376        # build data form
[116]377        form = data_form.Form('submit', formNamespace=NS_MUC_REQUEST)
[110]378        form.addField(data_form.Field(var='muc#role',
379                                      value='participant', 
380                                      label='Requested role'))
381        self.addChild(form.toElement())           
382
[111]383class PresenceError(xmppim.Presence):
[107]384    """
385    This behaves like an object providing L{domish.IElement}.
386
387    """
388
[108]389    def __init__(self, error, to=None, frm=None):
[111]390        xmppim.Presence.__init__(self, to, type='error')
[108]391        if frm:
392            self['from'] = frm
[107]393        # add muc elements
[116]394        x = self.addElement('x', NS_MUC)
[107]395        # add error
396        self.addChild(error)
397       
398
399class MUCClient(XMPPHandler):
400    """
401    Multi-User chat client protocol.
402    """
403
404    implements(IMUCClient)
405
[110]406    rooms = {}
[107]407
[118]408    timeout = None
409
410    _deferreds = []
411
[107]412    def connectionInitialized(self):
[113]413        self.xmlstream.addObserver(PRESENCE+"[not(@type) or @type='available']/x", self._onXPresence)
414        self.xmlstream.addObserver(PRESENCE+"[@type='unavailable']", self._onUnavailablePresence)
415        self.xmlstream.addObserver(PRESENCE+"[@type='error']", self._onPresenceError)
[110]416        self.xmlstream.addObserver(GROUPCHAT, self._onGroupChat)
417        self.xmlstream.addObserver(SUBJECT, self._onSubject)
418        # add history
[107]419
[115]420        self.initialized()
[114]421
[107]422    def _setRoom(self, room):
[114]423        self.rooms[room.entity_id.userhost().lower()] = room
[107]424
425    def _getRoom(self, room_jid):
[114]426        return self.rooms.get(room_jid.userhost().lower())
[107]427
[111]428    def _removeRoom(self, room_jid):
[114]429        if self.rooms.has_key(room_jid.userhost().lower()):
430            del self.rooms[room_jid.userhost().lower()]
[107]431
[113]432
433    def _onUnavailablePresence(self, prs):
434        """
435        """
[118]436
[113]437        if not prs.hasAttribute('from'):
438            return
439        room_jid = jid.internJID(prs.getAttribute('from', ''))
[114]440        self._userLeavesRoom(room_jid)
[113]441
442    def _onPresenceError(self, prs):
443        """
444        """
445        if not prs.hasAttribute('from'):
446            return
447        room_jid = jid.internJID(prs.getAttribute('from', ''))
[114]448        # add an error hook here?
449        self._userLeavesRoom(room_jid)
[113]450
[114]451    def _userLeavesRoom(self, room_jid):
452        room = self._getRoom(room_jid)
453        if room is None:
454            # not in the room yet
455            return
456        # check if user is in roster
457        user = room.getUser(room_jid.resource)
458        if user is None:
459            return
460        if room.inRoster(user):
461            room.removeUser(user)
462            self.userLeftRoom(room, user)
463       
[110]464    def _onXPresence(self, prs):
465        """
466        """
[113]467        if not prs.hasAttribute('from'):
468            return
469        room_jid = jid.internJID(prs.getAttribute('from', ''))
470           
471        status = getattr(prs, 'status', None)
472        show   = getattr(prs, 'show', None)
473       
474        # grab room
475        room = self._getRoom(room_jid)
476        if room is None:
477            # not in the room yet
478            return
479
480        # check if user is in roster
481        user = room.getUser(room_jid.resource)
482        if user is None: # create a user that does not exist
483            user = User(room_jid.resource)
484           
485       
486        if room.inRoster(user):
487            # we changed status or nick
488            muc_status = getattr(prs.x, 'status', None)
489            if muc_status:
490                code = muc_status.getAttribute('code', 0)
491            else:
492                self.userUpdatedStatus(room, user, show, status)
493        else:           
494            room.addUser(user)
495            self.userJoinedRoom(room, user)
[110]496           
497
498    def _onGroupChat(self, msg):
499        """
500        """
[113]501        if not msg.hasAttribute('from'):
502            # need to return an error here
503            return
504        room_jid = jid.internJID(msg.getAttribute('from', ''))
505
506        room = self._getRoom(room_jid)
507        if room is None:
508            # not in the room yet
509            return
510        user = room.getUser(room_jid.resource)
[116]511        delay = None
512        # need to check for delay and x stanzas for delay namespace for backwards compatability
513        for e in msg.elements():
514            if e.uri == NS_DELAY or e.uri == NS_JABBER_DELAY:
515                delay = e
[113]516        body  = unicode(msg.body)
517        # grab room
[112]518        if delay is None:
[113]519            self.receivedGroupChat(room, user, body)
[112]520        else:
[113]521            self.receivedHistory(room, user, body, delay['stamp'], frm=delay.getAttribute('from',None))
[110]522
523
524    def _onSubject(self, msg):
525        """
526        """
[113]527        if not msg.hasAttribute('from'):
528            return
529        room_jid = jid.internJID(msg['from'])
530
531        # grab room
532        room = self._getRoom(room_jid)
533        if room is None:
534            # not in the room yet
535            return
536
537        self.receivedSubject(room_jid, unicode(msg.subject))
[110]538
539
540    def _makeTimeStamp(self, stamp=None):
541        if stamp is None:
542            stamp = datetime.datetime.now()
543           
544        return stamp.strftime('%Y%m%dT%H:%M:%S')
545
[107]546
547    def _joinedRoom(self, d, prs):
548        """We have presence that says we joined a room.
549        """
550        room_jid = jid.internJID(prs['from'])
[108]551       
[107]552        # check for errors
553        if prs.hasAttribute('type') and prs['type'] == 'error':           
[108]554            d.errback(prs)
555        else:   
556            # change the state of the room
557            r = self._getRoom(room_jid)
[110]558            if r is None:
[111]559                raise Exception, 'Room Not Found' 
[108]560            r.state = 'joined'
[110]561           
562            # grab status
563            status = getattr(prs.x,'status',None)
564            if status:
565                r.status = status.getAttribute('code', None)
566
[108]567            d.callback(r)
[107]568
[111]569
570    def _leftRoom(self, d, prs):
571        """We have presence that says we joined a room.
572        """
573        room_jid = jid.internJID(prs['from'])
574       
575        # check for errors
576        if prs.hasAttribute('type') and prs['type'] == 'error':           
577            d.errback(prs)
578        else:   
579            # change the state of the room
580            r = self._getRoom(room_jid)
581            if r is None:
582                raise Exception, 'Room Not Found' 
583            self._removeRoom(room_jid)
584           
585            d.callback(True)
586
[115]587    def initialized(self):
588        """Client is initialized and ready!
589        """
590        pass
591
[113]592    def userJoinedRoom(self, room, user):
593        """User has joined a room
594        """
595        pass
596
[114]597    def userLeftRoom(self, room, user):
598        """User has left a room
599        """
600        pass
601
[113]602
[115]603    def userUpdatedStatus(self, room, user, show, status):
[107]604        """User Presence has been received
605        """
606        pass
607       
[108]608
[113]609    def receivedSubject(self, room, subject):
[110]610        """
611        """
612        pass
613
[112]614
[113]615    def receivedHistory(self, room, user, message, history, frm=None):
[112]616        """
617        """
618        pass
619
620
[108]621    def _cbDisco(self, iq):
622        # grab query
623       
[110]624        return getattr(iq,'query', None)
[108]625       
[118]626
627    def sendDeferred(self,  obj, timeout):
628        """ Send data or a domish element, adding a deferred with a timeout.
629        """
630        d = defer.Deferred()
631        self._deferreds.append(d)
632
633
634        def onTimeout():
635            i = 0
636            for xd in self._deferreds:
637                if d == xd:
638                    self._deferreds.pop(i)
639                    d.errback(xmlstream.TimeoutError("Timeout waiting for response."))
640                i += 1
641
642        call = reactor.callLater(timeout, onTimeout)
643       
644        def cancelTimeout(result):
645            if call.active():
646                call.cancel()
647
648            return result
649
650        d.addBoth(cancelTimeout)
651
652        self.xmlstream.send(obj)
653        return d
654
[108]655    def disco(self, entity, type='info'):
656        """Send disco queries to a XMPP entity
657        """
658
659        iq = disco.DiscoRequest(self.xmlstream, disco.NS_INFO, 'get')
660        iq['to'] = entity
661
[110]662        return iq.send().addBoth(self._cbDisco)
[108]663       
664
[111]665    def configure(self, room_jid, fields=[]):
[110]666        """Configure a room
[117]667
668        @param room_jid: The room jabber/xmpp entity id for the requested configuration form.
669        @type  room_jid: L{jid.JID}
670
[110]671        """
[111]672        request = ConfigureRequest(self.xmlstream, method='set', fields=fields)
[110]673        request['to'] = room_jid
674       
675        return request.send()
676
677    def getConfigureForm(self, room_jid):
[117]678        """Grab the configuration form from the room. This sends an iq request to the room.
679
680        @param room_jid: The room jabber/xmpp entity id for the requested configuration form.
681        @type  room_jid: L{jid.JID}
682
683        """
[110]684        request = ConfigureRequest(self.xmlstream)
685        request['to'] = room_jid
686        return request.send()
687
688
[108]689    def join(self, server, room, nick):
[117]690        """ Join a MUC room by sending presence to it. Returns a defered that is called when
691        the entity is in the room or an error has occurred.
692       
693        @param server: The server where the room is located.
694        @type  server: L{unicode}
695
696        @param room: The room name the entity is joining.
697        @type  room: L{unicode}
698
699        @param nick: The nick name for the entitity joining the room.
700        @type  nick: L{unicode}
701       
[107]702        """
703        r = Room(room, server, nick, state='joining')
704        self._setRoom(r)
705 
706        p = BasicPresence(to=r.entity_id)
[118]707        d = self.sendDeferred(p, timeout=DEFER_TIMEOUT)
[107]708
709        # add observer for joining the room
710        self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 
[108]711                                          self._joinedRoom, 1, d)
[107]712
713        return d
714   
[117]715    def _changedNick(self, d, room_jid, prs):
716        """Callback for changing the nick.
717        """
718
719        r = self._getRoom(room_jid)
720
721        d.callback(r)
722
723
724    def nick(self, room_jid, new_nick):
725        """ Change an entities nick name in a MUC room.
726       
727        See: http://xmpp.org/extensions/xep-0045.html#changenick
728
729        @param room_jid: The room jabber/xmpp entity id for the requested configuration form.
730        @type  room_jid: L{jid.JID}
731
732        @param new_nick: The nick name for the entitity joining the room.
733        @type  new_nick: L{unicode}
734       
735        """
736
[118]737       
[117]738        r = self._getRoom(room_jid)
739        if r is None:
740            raise Exception, 'Room not found'
741        r.nick = new_nick # change the nick
742        # create presence
743        # make sure we call the method to generate the new entity xmpp id
744        p = BasicPresence(to=r.entityId()) 
[118]745        d = self.sendDeferred(p, timeout=DEFER_TIMEOUT)
[117]746
747        # add observer for joining the room
748        self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 
749                                          self._changedNick, 1, d, room_jid)
750
751        return d
752       
[107]753
[111]754   
755    def leave(self, room_jid):
756        """
757        """
[112]758        r = self._getRoom(room_jid)
[111]759 
760        p = xmppim.UnavailablePresence(to=r.entity_id)
761
[118]762        d = self.sendDeferred(p, timeout=DEFER_TIMEOUT)
[111]763        # add observer for joining the room
[112]764        self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s' and @type='unavailable']" % (r.entity_id.full()), 
[111]765                                          self._leftRoom, 1, d)
766
767        return d
768   
769
770   
771
[112]772    def _sendMessage(self, msg, children=None):
[110]773
774        if children:
775            for c in children:
776                msg.addChild(c)
777       
778        self.xmlstream.send(msg)
779
[112]780    def groupChat(self, to, message, children=None):
781        """Send a groupchat message
782        """
783        msg = GroupChat(to, body=message)
784       
785        self._sendMessage(msg, children=children)
786
787    def chat(self, to, message, children=None):
788        msg = PrivateChat(to, body=message)
789
790        self._sendMessage(msg, children=children)
791       
792    def invite(self, to, reason=None, full_jid=None):
[113]793        """
794        """
[112]795        msg = InviteMessage(to, reason=reason, full_jid=full_jid)
796        self._sendMessage(msg)
797
798
799    def password(self, to, password):
800        p = PasswordPresence(to, password)
801
802        self.xmlstream.send(p)
[110]803   
[111]804    def register(self, to, fields=[]):
805        iq = RegisterRequest(self.xmlstream, method='set', fields=fields)
806        iq['to'] = to
807        return iq.send()
808
[113]809    def getRegisterForm(self, room):
810        """
811        """
[111]812        iq = RegisterRequest(self.xmlstream)
[113]813        iq['to'] = room.userhost()
[111]814        return iq.send()
815
[110]816    def subject(self, to, subject):
817        """
818        """
819        msg = GroupChat(to, subject=subject)
820        self.xmlstream.send(msg)
821
822    def voice(self, to):
823        """
824        """
825        msg = MessageVoice(to=to)
826        self.xmlstream.send(msg)
827
828
829    def history(self, to, message_list):
830        """
831        """
832       
833        for m in message_list:
834            m['type'] = 'groupchat'
835            mto = m['to']
836            frm = m.getAttribute('from', None)
837            m['to'] = to
838
839            d = m.addElement('delay', NS_DELAY)
840            d['stamp'] = self._makeTimeStamp()
841            d['from'] = mto
842
843            self.xmlstream.send(m)
844
[112]845    def ban(self, to, ban_jid, frm, reason=None):
846       
847        iq = AffiliationRequest(self.xmlstream,
848                                method='set',
849                                affiliation='outcast', 
850                                a_jid=ban_jid, 
851                                reason=reason)
852        iq['to'] = to.userhost() # this is a room jid, only send to room
853        iq['from'] = frm.full()
854        return iq.send()
855
856
857    def kick(self, to, kick_jid, frm, reason=None):
858       
859        iq = AffiliationRequest(self.xmlstream,
860                                method='set',
861                                a_jid=kick_jid, 
862                                reason=reason)
863        iq['to'] = to.userhost() # this is a room jid, only send to room
864        iq['from'] = frm.full()
865        return iq.send()
Note: See TracBrowser for help on using the repository browser.