source: wokkel/muc.py @ 119:053b615f0e11

wokkel-muc-client-support-24
Last change on this file since 119:053b615f0e11 was 119:053b615f0e11, checked in by Christopher Zorn <tofu@…>, 11 years ago

basic user methods are implemented, admin and owner are left

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