Changeset 129:29e517800b96
- Timestamp:
- Jun 4, 2009, 5:44:50 PM (13 years ago)
- Branch:
- wokkel-muc-client-support-24
- Location:
- wokkel
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
wokkel/muc.py
r127 r129 1 1 # -*- test-case-name: wokkel.test.test_muc -*- 2 2 # 3 # Copyright (c) 2003-200 8Ralph Meijer3 # Copyright (c) 2003-2009 Ralph Meijer 4 4 # See LICENSE for details. 5 5 … … 18 18 from twisted.words.xish import domish 19 19 20 from wokkel import disco, data_form, shim,xmppim21 from wokkel.subprotocols import IQHandlerMixin,XMPPHandler20 from wokkel import disco, data_form, xmppim 21 from wokkel.subprotocols import XMPPHandler 22 22 from wokkel.iwokkel import IMUCClient 23 23 … … 37 37 NS_REQUEST = 'jabber:iq:register' 38 38 39 # ad hoc commands40 NS_AD_HOC = "http://jabber.org/protocol/commands"41 42 43 39 # Iq get and set XPath queries 44 40 IQ = '/iq' … … 75 71 {'name':'fulljid', 76 72 'stanza':'presence', 77 73 78 74 }, 79 201: 80 {'name':'created', 75 201: 76 {'name':'created', 81 77 'stanza': 'presence', 82 78 'context':'Entering a room', 83 79 'purpose':'Inform user that a new room has been created' 84 }, 80 }, 85 81 } 86 82 … … 93 89 A jid malformed error from the server. 94 90 95 91 96 92 """ 97 93 condition = 'modify' 98 94 mucCondition = 'jid-malformed' 99 95 96 97 100 98 class NotAuthorized(Exception): 101 99 """ … … 104 102 mucCondition = 'not-authorized' 105 103 104 105 106 106 class RegistrationRequired(Exception): 107 107 """ … … 111 111 112 112 113 113 114 class Forbidden(Exception): 114 115 """ … … 117 118 mucCondition = 'forbidden' 118 119 120 121 119 122 class Conflict(Exception): 120 123 """ … … 123 126 mucCondition = 'conflict' 124 127 128 129 125 130 class NotFound(Exception): 126 131 """ … … 128 133 condition = 'cancel' 129 134 mucCondition = 'not-found' 135 136 130 137 131 138 class ServiceUnavailable(Exception): … … 147 154 } 148 155 156 157 149 158 class MUCError(error.StanzaError): 150 159 """ … … 161 170 162 171 172 163 173 class BadRequest(MUCError): 164 174 """ … … 196 206 form = data_form.Form('submit', formNamespace=NS_MUC_CONFIG) 197 207 q.addChild(form.toElement()) 198 208 199 209 for f in fields: 200 210 # create a field 201 211 form.addField(f) 212 202 213 203 214 … … 216 227 if method == 'set': 217 228 # build data form 218 form_type = 'submit' 229 form_type = 'submit' 219 230 form = data_form.Form(form_type, formNamespace=NS_MUC_REGISTER) 220 q.addChild(form.toElement()) 221 231 q.addChild(form.toElement()) 232 222 233 for f in fields: 223 234 # create a field … … 225 236 226 237 238 227 239 class AdminRequest(xmlstream.IQ): 228 240 """ 229 A basic admin iq request 241 A basic admin iq request 230 242 231 243 @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'} … … 239 251 240 252 253 241 254 class OwnerRequest(xmlstream.IQ): 242 255 """ 243 A basic owner iq request 256 A basic owner iq request 244 257 245 258 @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'} … … 252 265 q = self.addElement((NS_MUC_OWNER, 'query')) 253 266 254 267 255 268 256 269 class AffiliationRequest(AdminRequest): … … 283 296 i.addElement('reason', None, reason) 284 297 298 285 299 def items(self, jid_list=None): 286 """ Set or Get the items list for this affiliation request. 300 """ 301 Set or Get the items list for this affiliation request. 287 302 """ 288 303 if jid_list: … … 296 311 297 312 return self.query.elements() 298 299 313 314 300 315 301 316 class RoleRequest(AdminRequest): … … 312 327 if reason: 313 328 i.addElement('reason', None, reason) 314 329 330 331 315 332 class GroupChat(domish.Element): 316 """A message stanza of type groupchat. Used to send a message to a MUC room that will be 333 """ 334 A message stanza of type groupchat. Used to send a message to a MUC room that will be 317 335 broadcasted to people in the room. 318 336 … … 335 353 336 354 355 337 356 class PrivateChat(domish.Element): 338 """A chat message. 357 """ 358 A chat message. 339 359 """ 340 360 def __init__(self, to, body=None, frm=None): 341 """ Initialize the L{PrivateChat} 361 """ 362 Initialize the L{PrivateChat} 342 363 343 364 @param to: The xmpp entity you are sending this chat to. … … 361 382 if body: 362 383 self.addElement('body',None, body) 363 384 385 386 364 387 class InviteMessage(PrivateChat): 365 388 def __init__(self, to, reason=None, full_jid=None, body=None, frm=None, password=None): … … 375 398 invite.addElement('password', None, password) 376 399 400 401 377 402 class HistoryMessage(GroupChat): 378 """ A history message for MUC groupchat history. 403 """ 404 A history message for MUC groupchat history. 379 405 """ 380 406 def __init__(self, to, stamp, body=None, subject=None, frm=None, h_frm=None): … … 385 411 d['from'] = h_frm 386 412 413 414 387 415 class HistoryOptions(object): 388 """A history configuration object. 389 390 @ivar maxchars: Limit the total number of characters in the history to "X" 416 """ 417 A history configuration object. 418 419 @ivar maxchars: Limit the total number of characters in the history to "X" 391 420 (where the character count is the characters of the complete XML stanzas, not only their XML character data). 392 421 @type maxchars: L{int} 393 422 394 423 @ivar maxstanzas: Limit the total number of messages in the history to "X". 395 424 @type mazstanzas: L{int} … … 410 439 self.since = since 411 440 441 412 442 def toElement(self): 413 """Returns a L{domish.Element} representing the xml for the history options. 443 """ 444 Returns a L{domish.Element} representing the xml for the history options. 414 445 """ 415 446 h = domish.Element((None, 'history')) 416 447 for key in self.attributes: 417 a = getattr(self, key, a)448 a = getattr(self, key, None) 418 449 if a is not None: 419 450 if key == 'since': … … 421 452 else: 422 453 h[key] = str(a) 423 454 424 455 return h 425 456 457 458 426 459 class User(object): 427 460 """ 428 461 A user/entity in a multi-user chat room. 429 462 """ 430 463 431 464 def __init__(self, nick, user_jid=None): 432 465 self.nick = nick … … 434 467 self.affiliation = 'none' 435 468 self.role = 'none' 436 469 437 470 self.status = None 438 471 self.show = None 439 472 440 473 474 441 475 class Room(object): 442 476 """ … … 444 478 """ 445 479 446 480 447 481 def __init__(self, name, server, nick, state=None): 448 """ Initialize the room. 449 482 """ 483 Initialize the room. 484 450 485 @ivar name: The name of the MUC room. 451 486 @type name: L{unicode} … … 471 506 472 507 self.entity_id = self.entityId() 473 508 474 509 self.roster = {} 475 510 511 476 512 def entityId(self): 477 """ Creates the L{jid.JID} for the room. If name, server, or nick change then this method 513 """ 514 Creates the L{jid.JID} for the room. If name, server, or nick change then this method 478 515 should be called. 479 516 … … 481 518 self.entity_id = jid.internJID(self.name+'@'+self.server+'/'+self.nick) 482 519 483 return self.entity_id 520 return self.entity_id 521 484 522 485 523 def addUser(self, user): 486 """Add a user to the room roster. 524 """ 525 Add a user to the room roster. 487 526 488 527 @param user: The user object that is being added to the room. … … 492 531 self.roster[user.nick.lower()] = user 493 532 533 494 534 def inRoster(self, user): 495 """ Check if a user is in the MUC room. 535 """ 536 Check if a user is in the MUC room. 496 537 497 538 @param user: The user object to check. … … 502 543 return self.roster.has_key(user.nick.lower()) 503 544 545 504 546 def getUser(self, nick): 505 """ Get a user from the room's roster. 547 """ 548 Get a user from the room's roster. 506 549 507 550 @param nick: The nick for the user in the MUC room. … … 511 554 return self.roster.get(nick.lower()) 512 555 556 513 557 def removeUser(self, user): 514 """ Remove a user from the MUC room's roster. 515 558 """ 559 Remove a user from the MUC room's roster. 560 516 561 @param user: The user object to check. 517 562 @type user: L{User} 518 563 519 564 """ 520 565 if self.inRoster(user): 521 566 del self.roster[user.nick.lower()] 522 567 568 523 569 524 570 class BasicPresence(xmppim.AvailablePresence): … … 532 578 # add muc elements 533 579 x = self.addElement('x', NS_MUC) 580 534 581 535 582 … … 551 598 x['role'] = role 552 599 600 601 553 602 class UnavailableUserPresence(xmppim.UnavailablePresence): 554 603 """ … … 569 618 570 619 620 571 621 class PasswordPresence(BasicPresence): 572 """ This behaves like an object providing L{domish.IElement}. 622 """ 623 This behaves like an object providing L{domish.IElement}. 573 624 """ 574 625 def __init__(self, to, password): 575 626 BasicPresence.__init__(self, to) 576 627 577 628 self.x.addElement('password', None, password) 578 629 579 630 631 580 632 class MessageVoice(GroupChat): 581 """ This behaves like an object providing L{domish.IElement}. 633 """ 634 This behaves like an object providing L{domish.IElement}. 582 635 """ 583 636 def __init__(self, to=None, frm=None): … … 586 639 form = data_form.Form('submit', formNamespace=NS_MUC_REQUEST) 587 640 form.addField(data_form.Field(var='muc#role', 588 value='participant', 641 value='participant', 589 642 label='Requested role')) 590 self.addChild(form.toElement()) 643 self.addChild(form.toElement()) 644 645 591 646 592 647 class PresenceError(xmppim.Presence): … … 602 657 # add muc elements 603 658 x = self.addElement('x', NS_MUC) 604 605 # add error 659 660 # add error 606 661 e = error.getElement() 607 662 self.addChild(e) 608 663 664 609 665 610 666 class MUCClient(XMPPHandler): … … 623 679 624 680 def connectionInitialized(self): 625 """ This method is called when the client has successfully authenticated. 681 """ 682 This method is called when the client has successfully authenticated. 626 683 It initializes several xpath events to handle MUC stanzas that come in. 627 After those are initialized then the method initialized is called to signal that we have finished. 684 After those are initialized then the method initialized is called to signal that we have finished. 628 685 """ 629 686 self.xmlstream.addObserver(PRESENCE+"[not(@type) or @type='available']/x", self._onXPresence) … … 636 693 self.initialized() 637 694 695 638 696 def _setRoom(self, room): 639 """Add a room to the room collection. 697 """ 698 Add a room to the room collection. 640 699 """ 641 700 self.rooms[room.entity_id.userhost().lower()] = room 642 701 702 643 703 def _getRoom(self, room_jid): 644 """Grab a room from the room collection. 704 """ 705 Grab a room from the room collection. 645 706 """ 646 707 return self.rooms.get(room_jid.userhost().lower()) 647 708 709 648 710 def _removeRoom(self, room_jid): 649 """Delete a room from the room collection. 711 """ 712 Delete a room from the room collection. 650 713 """ 651 714 if self.rooms.has_key(room_jid.userhost().lower()): … … 654 717 655 718 def _onUnavailablePresence(self, prs): 656 """ This method is called when the stanza matches the xpath observer. 657 The client has received a presence stanza with the 'type' attribute of unavailable. 719 """ 720 This method is called when the stanza matches the xpath observer. 721 The client has received a presence stanza with the 'type' attribute of unavailable. 658 722 It means a user has exited a MUC room. 659 723 """ … … 664 728 self._userLeavesRoom(room_jid) 665 729 730 666 731 def _onPresenceError(self, prs): 667 """This method is called when a presence stanza with the 'type' attribute of error. 732 """ 733 This method is called when a presence stanza with the 'type' attribute of error. 668 734 There are various reasons for receiving a presence error and it means that the user has left the room. 669 735 """ … … 673 739 # add an error hook here? 674 740 self._userLeavesRoom(room_jid) 675 741 742 676 743 def _getExceptionFromElement(self, stanza): 677 744 # find an exception based on the error stanza … … 684 751 685 752 return MUC_EXCEPTIONS[muc_condition] 753 686 754 687 755 def _userLeavesRoom(self, room_jid): … … 698 766 room.removeUser(user) 699 767 self.userLeftRoom(room, user) 700 768 769 701 770 def _onXPresence(self, prs): 702 """ A muc presence has been received. 771 """ 772 A muc presence has been received. 703 773 """ 704 774 if not prs.hasAttribute('from'): 705 775 return 706 776 room_jid = jid.internJID(prs.getAttribute('from', '')) 707 777 708 778 status = getattr(prs, 'status', None) 709 779 show = getattr(prs, 'show', None) 710 780 711 781 # grab room 712 782 room = self._getRoom(room_jid) … … 717 787 718 788 if room.inRoster(user): 719 # we changed status or nick 789 # we changed status or nick 720 790 muc_status = getattr(prs.x, 'status', None) 721 791 if muc_status: … … 723 793 else: 724 794 self.userUpdatedStatus(room, user, show, status) 725 else: 795 else: 726 796 room.addUser(user) 727 797 self.userJoinedRoom(room, user) 728 798 729 799 730 800 def _onGroupChat(self, msg): 731 """ A group chat message has been received from a MUC room. 732 801 """ 802 A group chat message has been received from a MUC room. 803 733 804 There are a few event methods that may get called here. receviedGroupChat and receivedHistory 734 805 """ … … 757 828 758 829 def _onSubject(self, msg): 759 """A subject has been sent from a MUC room. 830 """ 831 A subject has been sent from a MUC room. 760 832 """ 761 833 if not msg.hasAttribute('from'): … … 776 848 if stamp is None: 777 849 stamp = datetime.datetime.now() 778 850 779 851 return stamp.strftime('%Y%m%dT%H:%M:%S') 780 852 781 853 782 854 def _joinedRoom(self, d, prs): 783 """We have presence that says we joined a room. 855 """ 856 We have presence that says we joined a room. 784 857 """ 785 858 room_jid = jid.internJID(prs['from']) 786 859 787 860 # check for errors 788 if prs.hasAttribute('type') and prs['type'] == 'error': 861 if prs.hasAttribute('type') and prs['type'] == 'error': 789 862 d.errback(self._getExceptionFromElement(prs)) 790 else: 863 else: 791 864 # change the state of the room 792 865 r = self._getRoom(room_jid) … … 794 867 raise NotFound 795 868 r.state = 'joined' 796 869 797 870 # grab status 798 871 status = getattr(prs.x,'status',None) … … 804 877 805 878 def _leftRoom(self, d, prs): 806 """We have presence that says we joined a room. 879 """ 880 We have presence that says we joined a room. 807 881 """ 808 882 room_jid = jid.internJID(prs['from']) 809 883 810 884 # check for errors 811 if prs.hasAttribute('type') and prs['type'] == 'error': 885 if prs.hasAttribute('type') and prs['type'] == 'error': 812 886 d.errback(self._getExceptionFromElement(prs)) 813 else: 887 else: 814 888 # change the state of the room 815 889 r = self._getRoom(room_jid) … … 817 891 raise NotFound 818 892 self._removeRoom(room_jid) 819 893 820 894 d.callback(True) 821 895 896 822 897 def initialized(self): 823 """Client is initialized and ready! 898 """ 899 Client is initialized and ready! 824 900 """ 825 901 pass 826 902 903 827 904 def userJoinedRoom(self, room, user): 828 """User has joined a MUC room. 905 """ 906 User has joined a MUC room. 829 907 830 908 This method will need to be modified inorder for clients to … … 840 918 pass 841 919 920 842 921 def userLeftRoom(self, room, user): 843 """User has left a room. 844 922 """ 923 User has left a room. 924 845 925 This method will need to be modified inorder for clients to 846 926 do something when this event occurs. … … 857 937 858 938 def userUpdatedStatus(self, room, user, show, status): 859 """User Presence has been received 860 939 """ 940 User Presence has been received 941 861 942 This method will need to be modified inorder for clients to 862 943 do something when this event occurs. 863 944 864 945 """ 865 946 pass 866 947 867 948 868 949 def receivedSubject(self, room, subject): … … 885 966 def _cbDisco(self, iq): 886 967 # grab query 887 968 888 969 return getattr(iq,'query', None) 889 970 890 971 891 972 def sendDeferred(self, obj, timeout): 892 """ Send data or a domish element, adding a deferred with a timeout. 893 973 """ 974 Send data or a domish element, adding a deferred with a timeout. 975 894 976 @param obj: The object to send over the wire. 895 977 @type obj: L{domish.Element} or L{unicode} … … 913 995 914 996 call = reactor.callLater(timeout, onTimeout) 915 997 916 998 def cancelTimeout(result): 917 999 if call.active(): … … 925 1007 return d 926 1008 1009 927 1010 def disco(self, entity, type='info'): 928 """Send disco queries to a XMPP entity. 1011 """ 1012 Send disco queries to a XMPP entity. 929 1013 930 1014 @param entity: The server or entity where we want discovery information from. … … 940 1024 941 1025 return iq.send().addBoth(self._cbDisco) 942 1026 943 1027 944 1028 def configure(self, room_jid, fields=[]): 945 """Configure a room 1029 """ 1030 Configure a room 946 1031 947 1032 @param room_jid: The room jabber/xmpp entity id for the requested configuration form. … … 954 1039 request = ConfigureRequest(self.xmlstream, method='set', fields=fields) 955 1040 request['to'] = room_jid 956 1041 957 1042 return request.send() 958 1043 1044 959 1045 def getConfigureForm(self, room_jid): 960 """Grab the configuration form from the room. This sends an iq request to the room. 1046 """ 1047 Grab the configuration form from the room. This sends an iq request to the room. 961 1048 962 1049 @param room_jid: The room jabber/xmpp entity id for the requested configuration form. … … 970 1057 971 1058 def join(self, server, room, nick, history = None): 972 """ Join a MUC room by sending presence to it. Returns a defered that is called when 973 the entity is in the room or an error has occurred. 974 1059 """ 1060 Join a MUC room by sending presence to it. Returns a defered that is called when 1061 the entity is in the room or an error has occurred. 1062 975 1063 @param server: The server where the room is located. 976 1064 @type server: L{unicode} … … 981 1069 @param nick: The nick name for the entitity joining the room. 982 1070 @type nick: L{unicode} 983 1071 984 1072 @param history: The maximum number of history stanzas you would like. 985 1073 … … 987 1075 r = Room(room, server, nick, state='joining') 988 1076 self._setRoom(r) 989 1077 990 1078 p = BasicPresence(to=r.entity_id) 991 1079 if history is not None: … … 995 1083 996 1084 # add observer for joining the room 997 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 1085 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 998 1086 self._joinedRoom, 1, d) 999 1087 1000 1088 return d 1001 1089 1090 1002 1091 def _changeUserStatus(self, r, room_jid, status, show): 1003 1092 # change the user status in a room. … … 1015 1104 return user 1016 1105 1106 1017 1107 def _changed(self, d, room_jid, prs): 1018 """Callback for changing the nick and status. 1108 """ 1109 Callback for changing the nick and status. 1019 1110 """ 1020 1111 … … 1030 1121 1031 1122 def nick(self, room_jid, new_nick): 1032 """ Change an entities nick name in a MUC room. 1033 1123 """ 1124 Change an entities nick name in a MUC room. 1125 1034 1126 See: http://xmpp.org/extensions/xep-0045.html#changenick 1035 1127 … … 1039 1131 @param new_nick: The nick name for the entitity joining the room. 1040 1132 @type new_nick: L{unicode} 1041 1042 """ 1043 1044 1133 1134 """ 1135 1136 1045 1137 r = self._getRoom(room_jid) 1046 1138 if r is None: 1047 1139 raise NotFound 1048 1140 r.nick = new_nick # change the nick 1049 # create presence 1141 # create presence 1050 1142 # make sure we call the method to generate the new entity xmpp id 1051 p = BasicPresence(to=r.entityId()) 1143 p = BasicPresence(to=r.entityId()) 1052 1144 d = self.sendDeferred(p, timeout=DEFER_TIMEOUT) 1053 1145 1054 1146 # add observer for joining the room 1055 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 1147 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 1056 1148 self._changed, 1, d, room_jid) 1057 1149 1058 1150 return d 1059 1060 1061 1151 1152 1062 1153 def leave(self, room_jid): 1063 """Leave a MUC room. 1154 """ 1155 Leave a MUC room. 1064 1156 1065 1157 See: http://xmpp.org/extensions/xep-0045.html#exit … … 1070 1162 """ 1071 1163 r = self._getRoom(room_jid) 1072 1164 1073 1165 p = xmppim.UnavailablePresence(to=r.entity_id) 1074 1166 1075 1167 d = self.sendDeferred(p, timeout=DEFER_TIMEOUT) 1076 1168 # add observer for joining the room 1077 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s' and @type='unavailable']" % (r.entity_id.full()), 1169 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s' and @type='unavailable']" % (r.entity_id.full()), 1078 1170 self._leftRoom, 1, d) 1079 1171 1080 1172 return d 1081 1173 1082 1174 1083 1175 def status(self, room_jid, show=None, status=None): 1084 """Change user status. 1176 """ 1177 Change user status. 1085 1178 1086 1179 See: http://xmpp.org/extensions/xep-0045.html#changepres … … 1092 1185 @type show: L{unicode} 1093 1186 1094 @param show: The current status of the entity. 1187 @param show: The current status of the entity. 1095 1188 @type show: L{unicode} 1096 1189 … … 1100 1193 raise NotFound 1101 1194 1102 p = BasicPresence(to=r.entityId()) 1195 p = BasicPresence(to=r.entityId()) 1103 1196 if status is not None: 1104 1197 p.addElement('status', None, status) 1105 1198 1106 1199 if show is not None: 1107 1200 p.addElement('show', None, show) 1108 1201 1109 1202 d = self.sendDeferred(p, timeout=DEFER_TIMEOUT) 1110 1203 1111 1204 # add observer for joining the room 1112 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 1205 self.xmlstream.addOnetimeObserver(PRESENCE+"[@from='%s']" % (r.entity_id.full()), 1113 1206 self._changed, 1, d, room_jid) 1114 1207 1115 1208 return d 1209 1116 1210 1117 1211 def _sendMessage(self, msg, children=None): … … 1120 1214 for c in children: 1121 1215 msg.addChild(c) 1122 1216 1123 1217 self.xmlstream.send(msg) 1124 1218 1219 1125 1220 def groupChat(self, to, message, children=None): 1126 """Send a groupchat message 1221 """ 1222 Send a groupchat message 1127 1223 """ 1128 1224 msg = GroupChat(to, body=message) 1129 1225 1130 1226 self._sendMessage(msg, children=children) 1131 1227 1228 1132 1229 def chat(self, room_jid, message, children=None): 1133 """Send a private chat message to a user in a MUC room. 1134 1230 """ 1231 Send a private chat message to a user in a MUC room. 1232 1135 1233 See: http://xmpp.org/extensions/xep-0045.html#privatemessage 1136 1234 … … 1143 1241 1144 1242 self._sendMessage(msg, children=children) 1145 1243 1244 1146 1245 def invite(self, room_jid, reason=None, full_jid=None): 1147 """Invite a xmpp entity to a MUC room. 1246 """ 1247 Invite a xmpp entity to a MUC room. 1148 1248 1149 1249 See: http://xmpp.org/extensions/xep-0045.html#invite … … 1151 1251 @param room_jid: The room entity id. 1152 1252 @type room_jid: L{jid.JID} 1153 1253 1154 1254 @param reason: The reason for the invite. 1155 1255 @type reason: L{unicode} … … 1164 1264 1165 1265 def password(self, room_jid, password): 1166 """Send a password to a room so the entity can join. 1167 1266 """ 1267 Send a password to a room so the entity can join. 1268 1168 1269 See: http://xmpp.org/extensions/xep-0045.html#enter-pw 1169 1270 1170 1271 @param room_jid: The room entity id. 1171 1272 @type room_jid: L{jid.JID} 1172 1273 1173 1274 @param password: The MUC room password. 1174 1275 @type password: L{unicode} 1175 1276 1176 1277 """ 1177 1278 p = PasswordPresence(room_jid, password) 1178 1279 1179 1280 self.xmlstream.send(p) 1180 1281 1282 1181 1283 def register(self, room_jid, fields=[]): 1182 """ Send a request to register for a room. 1284 """ 1285 Send a request to register for a room. 1183 1286 1184 1287 @param room_jid: The room entity id. … … 1198 1301 iq = AffiliationRequest(self.xmlstream, 1199 1302 method='get', 1200 affiliation=affiliation, 1303 affiliation=affiliation, 1201 1304 ) 1202 1305 iq['to'] = room_jid.full() 1203 return iq.send() 1306 return iq.send() 1204 1307 1205 1308 … … 1211 1314 ) 1212 1315 iq['to'] = room_jid.full() 1213 return iq.send() 1316 return iq.send() 1214 1317 1215 1318 … … 1220 1323 affiliation_list = [] 1221 1324 setattr(r, affiliation, []) 1222 1325 1223 1326 for item in iq.query.elements(): 1224 1327 nick = item.getAttribute('nick', None) … … 1228 1331 raise Exception, 'bad attributes in item list' 1229 1332 if nick is not None: 1230 u = r oom.getUser(nick)1333 u = r.getUser(nick) 1231 1334 if u is None: 1232 1335 u = User(nick, user_jid=jid.internJID(entity)) 1233 1336 u.affiliation = 'member' 1234 1337 1235 1338 affiliation_list.append(u) 1236 1339 … … 1238 1341 return r 1239 1342 1343 1240 1344 def getMemberList(self, room_jid): 1241 """ Get a member list from a room. 1345 """ 1346 Get a member list from a room. 1242 1347 1243 1348 @param room_jid: The room jabber/xmpp entity id for the requested member list. … … 1249 1354 return d 1250 1355 1356 1251 1357 def getAdminList(self, room_jid): 1252 """ Get an admin list from a room. 1358 """ 1359 Get an admin list from a room. 1253 1360 1254 1361 @param room_jid: The room jabber/xmpp entity id for the requested member list. … … 1260 1367 return d 1261 1368 1369 1262 1370 def getBanList(self, room_jid): 1263 """ Get an outcast list from a room. 1371 """ 1372 Get an outcast list from a room. 1264 1373 1265 1374 @param room_jid: The room jabber/xmpp entity id for the requested member list. … … 1271 1380 return d 1272 1381 1382 1273 1383 def getOwnerList(self, room_jid): 1274 """ Get an owner list from a room. 1384 """ 1385 Get an owner list from a room. 1275 1386 1276 1387 @param room_jid: The room jabber/xmpp entity id for the requested member list. … … 1282 1393 return d 1283 1394 1395 1284 1396 def getRegisterForm(self, room): 1285 """ Grab the registration form for a MUC room. 1397 """ 1398 Grab the registration form for a MUC room. 1286 1399 1287 1400 @param room: The room jabber/xmpp entity id for the requested registration form. … … 1293 1406 return iq.send() 1294 1407 1408 1295 1409 def destroy(self, room_jid, reason=None): 1296 """ Destroy a room. 1297 1410 """ 1411 Destroy a room. 1412 1298 1413 @param room_jid: The room jabber/xmpp entity id. 1299 1414 @type room_jid: L{jid.JID} 1300 1415 1301 1416 @ivar reason: The reason we are destroying the room. 1302 1417 @type reason: L{unicode} … … 1315 1430 return iq.send().addCallback(destroyed) 1316 1431 1432 1317 1433 def subject(self, room_jid, subject): 1318 """ Change the subject of a MUC room. 1434 """ 1435 Change the subject of a MUC room. 1319 1436 1320 1437 See: http://xmpp.org/extensions/xep-0045.html#subject-mod … … 1330 1447 self.xmlstream.send(msg) 1331 1448 1449 1332 1450 def voice(self, room_jid): 1333 """ Request voice for a moderated room. 1451 """ 1452 Request voice for a moderated room. 1334 1453 1335 1454 @param room_jid: The room jabber/xmpp entity id. … … 1341 1460 1342 1461 1343 1344 1462 def history(self, room_jid, message_list): 1345 """ Send history to create a MUC based on a one on one chat. 1346 1463 """ 1464 Send history to create a MUC based on a one on one chat. 1465 1347 1466 See: http://xmpp.org/extensions/xep-0045.html#continue 1348 1467 … … 1354 1473 1355 1474 """ 1356 1475 1357 1476 for m in message_list: 1358 1477 m['type'] = 'groupchat' … … 1363 1482 d = m.addElement('delay', NS_DELAY) 1364 1483 d['stamp'] = self._makeTimeStamp() 1365 d['from'] = mto 1484 d['from'] = mto 1366 1485 1367 1486 self.xmlstream.send(m) 1487 1368 1488 1369 1489 def _setAffiliation(self, frm, room_jid, affiliation, reason=None, a_jid=None, nick=None): … … 1372 1492 method='set', 1373 1493 affiliation=affiliation, 1374 a_jid=a_jid, 1494 a_jid=a_jid, 1375 1495 nick=nick, 1376 1496 reason=reason) … … 1378 1498 iq['from'] = frm.full() 1379 1499 return iq.send() 1500 1380 1501 1381 1502 def _setRole(self, frm, room_jid, a_jid=None, reason=None, role='none', nick=None): … … 1387 1508 nick=nick, 1388 1509 reason=reason) 1389 1510 1390 1511 iq['to'] = room_jid.userhost() # this is a room jid, only send to room 1391 1512 iq['from'] = frm.full() 1392 1513 return iq.send() 1514 1393 1515 1394 1516 def _cbRequest(self, room_jid, iq): … … 1399 1521 return r 1400 1522 1523 1401 1524 def modifyAffiliationList(self, frm, room_jid, jid_list, affiliation): 1402 """Modify an affiliation list. 1525 """ 1526 Modify an affiliation list. 1403 1527 1404 1528 @param frm: The entity sending the request. … … 1425 1549 return iq.send() 1426 1550 1551 1427 1552 def grantVoice(self, frm, room_jid, voice_jid=None, reason=None, nick=None): 1428 """ Grant voice to an entity. 1429 1553 """ 1554 Grant voice to an entity. 1555 1430 1556 @param frm: The entity sending the request. 1431 1557 @type frm: L{jid.JID} … … 1433 1559 @param room_jid: The room jabber/xmpp entity id. 1434 1560 @type room_jid: L{jid.JID} 1435 1561 1436 1562 @param reason: The reason for granting voice to the entity. 1437 1563 @type reason: L{unicode} … … 1443 1569 return self._setRole(frm, room_jid, role='participant', a_jid=voice_jid, nick=nick, reason=reason) 1444 1570 1571 1445 1572 def grantVisitor(self, frm, room_jid, reason=None, nick=None): 1446 """ Change a participant to a visitor. This will disallow the entity to send messages to a moderated room. 1573 """ 1574 Change a participant to a visitor. This will disallow the entity to send messages to a moderated room. 1447 1575 @param frm: The entity sending the request. 1448 1576 @type frm: L{jid.JID} … … 1450 1578 @param room_jid: The room jabber/xmpp entity id. 1451 1579 @type room_jid: L{jid.JID} 1452 1580 1453 1581 @param reason: The reason for granting voice to the entity. 1454 1582 @type reason: L{unicode} … … 1460 1588 return self._setRole(frm, room_jid, role='visitor', reason=reason, nick=nick) 1461 1589 1590 1462 1591 def grantModerator(self, frm, room_jid, reason=None, nick=None): 1463 """Grant moderator priviledges to a MUC room. 1592 """ 1593 Grant moderator priviledges to a MUC room. 1464 1594 1465 1595 @param frm: The entity sending the request. … … 1472 1602 return self._setRole(frm, room_jid, role='moderator', reason=reason, nick=nick) 1473 1603 1604 1474 1605 def ban(self, room_jid, ban_jid, frm, reason=None, nick=None): 1475 """Ban a user from a MUC room. 1606 """ 1607 Ban a user from a MUC room. 1476 1608 1477 1609 @param room_jid: The room jabber/xmpp entity id. … … 1493 1625 return self._setAffiliation(frm, room_jid, 'outcast', nick=nick, a_jid=ban_jid, reason=reason) 1494 1626 1495 def kick(self, room_jid, kick_jid, frm, reason=None, nick=None): 1496 """Kick a user from a MUC room. 1627 1628 def kick(self, room_jid, kick_jid, frm, reason=None, nick=None): 1629 """ 1630 Kick a user from a MUC room. 1497 1631 1498 1632 @param room_jid: The room jabber/xmpp entity id. … … 1513 1647 """ 1514 1648 return self._setAffiliation(frm, room_jid, 'none', a_jid=kick_jid, nick=nick, reason=reason) 1515 -
wokkel/test/test_muc.py
r127 r129 1 # Copyright (c) 2003-200 8Ralph Meijer1 # Copyright (c) 2003-2009 Ralph Meijer 2 2 # See LICENSE for details. 3 3 … … 11 11 from twisted.internet import defer 12 12 from twisted.words.xish import domish, xpath 13 from twisted.words.protocols.jabber import error14 13 from twisted.words.protocols.jabber.jid import JID 15 14 16 from wokkel import data_form, iwokkel, muc, shim, disco 17 from wokkel.generic import parseXml 15 from wokkel import data_form, iwokkel, muc, disco 18 16 from wokkel.test.helpers import XmlStreamStub 19 17 20 try: 21 from twisted.words.protocols.jabber.xmlstream import toResponse 22 except ImportError: 23 from wokkel.compat import toResponse 18 from twisted.words.protocols.jabber.xmlstream import toResponse 24 19 25 20 … … 39 34 40 35 return d, func 36 41 37 42 38 … … 57 53 self.user_jid = JID('test@jabber.org/Testing') 58 54 55 59 56 def _createRoom(self): 60 57 """A helper method to create a test room. … … 81 78 """ 82 79 p = muc.UserPresence() 83 80 p['to'] = self.user_jid.full() 84 81 p['from'] = self.room_jid.full() 85 82 … … 90 87 self.failUnless(room.name==self.test_room, 'Wrong room name') 91 88 self.failUnless(room.inRoster(user), 'User not in roster') 92 93 89 90 94 91 d, self.protocol.userJoinedRoom = calledAsync(userPresence) 95 92 self.stub.send(p) … … 101 98 """ 102 99 m = muc.GroupChat('test@test.com',body='test') 103 100 m['from'] = self.room_jid.full() 104 101 105 102 self._createRoom() … … 108 105 self.failUnless(message=='test', "Wrong group chat message") 109 106 self.failUnless(room.name==self.test_room, 'Wrong room name') 110 111 107 108 112 109 d, self.protocol.receivedGroupChat = calledAsync(groupChat) 113 110 self.stub.send(m) … … 123 120 # check namespace 124 121 self.failUnless(query.uri==disco.NS_INFO, 'Wrong namespace') 125 122 126 123 127 124 d = self.protocol.disco(test_srv) … … 129 126 130 127 iq = self.stub.output[-1] 131 128 132 129 # send back a response 133 130 response = toResponse(iq, 'result') … … 138 135 name='Macbeth Chat Service', 139 136 type='text')) 140 141 self.stub.send(response) 142 return d 143 144 145 137 138 self.stub.send(response) 139 return d 140 141 146 142 def test_joinRoom(self): 147 143 """Joining a room 148 144 """ 149 145 150 146 def cb(room): 151 147 self.assertEquals(self.test_room, room.name) … … 158 154 self.failUnless(getattr(prs, 'x', None), 'No muc x element') 159 155 160 # send back user presence, they joined 156 # send back user presence, they joined 161 157 response = muc.UserPresence(frm=self.test_room+'@'+self.test_srv+'/'+self.test_nick) 162 158 self.stub.send(response) 163 159 return d 164 160 165 166 161 167 162 def test_joinRoomForbidden(self): … … 170 165 171 166 def cb(error): 172 167 173 168 self.failUnless(error.value.mucCondition=='forbidden','Wrong muc condition') 174 169 175 176 170 171 177 172 d = self.protocol.join(self.test_srv, self.test_room, self.test_nick) 178 173 d.addBoth(cb) … … 182 177 self.failUnless(getattr(prs, 'x', None), 'No muc x element') 183 178 # send back user presence, they joined 184 179 185 180 response = muc.PresenceError(error=muc.MUCError('auth', 186 181 'forbidden' … … 188 183 frm=self.room_jid.full()) 189 184 self.stub.send(response) 190 return d 185 return d 191 186 192 187 … … 196 191 197 192 def cb(error): 198 193 199 194 self.failUnless(error.value.mucCondition=='jid-malformed','Wrong muc condition') 200 195 201 202 196 197 203 198 d = self.protocol.join(self.test_srv, self.test_room, self.test_nick) 204 199 d.addBoth(cb) … … 208 203 self.failUnless(getattr(prs, 'x', None), 'No muc x element') 209 204 # send back user presence, they joined 210 205 211 206 response = muc.PresenceError(error=muc.MUCError('modify', 212 207 'jid-malformed' … … 214 209 frm=self.room_jid.full()) 215 210 self.stub.send(response) 216 return d 217 211 return d 218 212 219 213 … … 229 223 230 224 prs = self.stub.output[-1] 231 225 232 226 self.failUnless(prs['type']=='unavailable', 'Unavailable is not being sent') 233 227 234 228 response = prs 235 229 response['from'] = response['to'] … … 238 232 self.stub.send(response) 239 233 return d 240 234 241 235 242 236 def test_userPartsRoom(self): … … 245 239 246 240 p = muc.UnavailableUserPresence() 247 241 p['to'] = self.user_jid.full() 248 242 p['from'] = self.room_jid.full() 249 243 … … 259 253 self.failUnless(room.name==self.test_room, 'Wrong room name') 260 254 self.failUnless(room.inRoster(user)==False, 'User in roster') 261 255 262 256 d, self.protocol.userLeftRoom = calledAsync(userPresence) 263 257 self.stub.send(p) 264 258 return d 265 259 266 260 267 261 def test_ban(self): … … 272 266 self.failUnless(banned, 'Did not ban user') 273 267 274 268 275 269 d = self.protocol.ban(self.room_jid, banned, self.user_jid, reason='Spam') 276 270 d.addCallback(cb) 277 271 278 272 iq = self.stub.output[-1] 279 273 280 274 self.failUnless(xpath.matches("/iq[@type='set' and @to='%s']/query/item[@affiliation='outcast']" % (self.room_jid.userhost(),), iq), 'Wrong ban stanza') 281 275 … … 294 288 self.failUnless(kicked, 'Did not kick user') 295 289 296 290 297 291 d = self.protocol.kick(self.room_jid, kicked, self.user_jid, reason='Spam') 298 292 d.addCallback(cb) 299 293 300 294 iq = self.stub.output[-1] 301 295 302 296 self.failUnless(xpath.matches("/iq[@type='set' and @to='%s']/query/item[@affiliation='none']" % (self.room_jid.userhost(),), iq), 'Wrong kick stanza') 303 297 … … 308 302 return d 309 303 310 311 304 312 305 def test_password(self): 313 306 """Sending a password via presence to a password protected room. 314 307 """ 315 308 316 309 self.protocol.password(self.room_jid, 'secret') 317 318 prs = self.stub.output[-1] 319 310 311 prs = self.stub.output[-1] 312 320 313 self.failUnless(xpath.matches("/presence[@to='%s']/x/password[text()='secret']" % (self.room_jid.full(),), prs), 'Wrong presence stanza') 321 314 … … 325 318 """ 326 319 m = muc.HistoryMessage(self.room_jid.userhost(), self.protocol._makeTimeStamp(), body='test') 327 328 320 m['from'] = self.room_jid.full() 321 329 322 self._createRoom() 330 323 … … 332 325 self.failUnless(body=='test', "wrong message body") 333 326 self.failUnless(stamp, 'Does not have a history stamp') 334 327 335 328 336 329 d, self.protocol.receivedHistory = calledAsync(roomHistory) … … 369 362 self.failUnless(m.name=='message', 'Wrong stanza') 370 363 self.failUnless(xpath.matches("/message/delay", m), 'Invalid history stanza') 371 364 372 365 373 366 def test_invite(self): … … 383 376 384 377 385 386 378 def test_privateMessage(self): 387 379 """Send private messages to muc entities. … … 400 392 401 393 """ 402 394 403 395 def cb(iq): 404 396 # check for a result 405 397 self.failUnless(iq['type']=='result', 'We did not get a result') 406 398 407 399 d = self.protocol.register(self.room_jid) 408 400 d.addCallback(cb) … … 410 402 iq = self.stub.output[-1] 411 403 self.failUnless(xpath.matches("/iq/query[@xmlns='%s']" % (muc.NS_REQUEST), iq), 'Invalid iq register request') 412 413 response = toResponse(iq, 'result') 414 415 self.stub.send(response) 416 return d 404 405 response = toResponse(iq, 'result') 406 407 self.stub.send(response) 408 return d 409 417 410 418 411 def test_voice(self): … … 422 415 423 416 m = self.stub.output[-1] 424 417 425 418 self.failUnless(xpath.matches("/message/x[@type='submit']/field/value[text()='%s']" % (muc.NS_MUC_REQUEST,), m), 'Invalid voice message stanza') 426 419 … … 432 425 def cb(iq): 433 426 self.failUnless(iq['type']=='result', 'Not a result') 434 427 435 428 436 429 fields = [] … … 439 432 var='muc#roomconfig_roomname', 440 433 value=self.test_room)) 441 434 442 435 d = self.protocol.configure(self.room_jid.userhost(), fields) 443 436 d.addCallback(cb) … … 445 438 iq = self.stub.output[-1] 446 439 self.failUnless(xpath.matches("/iq/query[@xmlns='%s']/x"% (muc.NS_MUC_OWNER,), iq), 'Bad configure request') 447 440 448 441 response = toResponse(iq, 'result') 449 442 self.stub.send(response) … … 457 450 def cb(destroyed): 458 451 self.failUnless(destroyed==True, 'Room not destroyed.') 459 452 460 453 d = self.protocol.destroy(self.room_jid) 461 454 d.addCallback(cb) … … 463 456 iq = self.stub.output[-1] 464 457 self.failUnless(xpath.matches("/iq/query[@xmlns='%s']/destroy"% (muc.NS_MUC_OWNER,), iq), 'Bad configure request') 465 458 466 459 response = toResponse(iq, 'result') 467 460 self.stub.send(response) … … 473 466 """ 474 467 test_nick = 'newNick' 475 468 476 469 self._createRoom() 477 470 … … 487 480 self.failUnless(getattr(prs, 'x', None), 'No muc x element') 488 481 489 # send back user presence, they joined 482 # send back user presence, they joined 490 483 response = muc.UserPresence(frm=self.test_room+'@'+self.test_srv+'/'+test_nick) 491 492 self.stub.send(response) 493 return d 484 485 self.stub.send(response) 486 return d 487 494 488 495 489 def test_grantVoice(self): … … 501 495 self.failUnless(give_voice, 'Did not give voice user') 502 496 503 497 504 498 d = self.protocol.grantVoice(self.user_jid, self.room_jid, give_voice) 505 499 d.addCallback(cb) 506 500 507 501 iq = self.stub.output[-1] 508 502 509 503 self.failUnless(xpath.matches("/iq[@type='set' and @to='%s']/query/item[@role='participant']" % (self.room_jid.userhost(),), iq), 'Wrong voice stanza') 510 504 … … 530 524 self.failUnless(u.status == 'testing MUC', 'Wrong status') 531 525 self.failUnless(u.show == 'xa', 'Wrong show') 532 526 533 527 d = self.protocol.status(self.room_jid, 'xa', 'testing MUC') 534 528 d.addCallback(cb) … … 539 533 self.failUnless(getattr(prs, 'x', None), 'No muc x element') 540 534 541 # send back user presence, they joined 535 # send back user presence, they joined 542 536 response = muc.UserPresence(frm=self.room_jid.full()) 543 537 response.addElement('show', None, 'xa') 544 538 response.addElement('status', None, 'testing MUC') 545 539 self.stub.send(response) 546 return d 540 return d
Note: See TracChangeset
for help on using the changeset viewer.