Ignore:
Timestamp:
Sep 2, 2011, 9:56:49 AM (11 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
wokkel-muc-client-support-24
Message:

Improve UserPresence? parsing, clean up MUCClient, approach full test coverage.

  • Parse muc#user item elements from incoming presence.
  • Properly interpret status codes in muc#user extension; there can be more than one and they should be used as integers.
  • Give observers of responses to joins, leaves and nick changes a lower priority than the general presence observers inherited from PresenceProtocol?. This ensures that the Room/User? administration in MUCClient is processed before the deferred for these actions fire.
  • Use roomJID to create Room instances, instead of the separate roomIdentifier and server arguments. Also store this roomJID on the Room.
  • Change prototype for receivedSubject to include user argument.
  • Make several callback functions nested functions within the methods that use them, instead of method on the class itself.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • wokkel/muc.py

    r152 r153  
    356356    """
    357357
    358     statusCode = None
     358    affiliation = None
     359    role = None
     360    entity = None
     361    nick = None
     362
     363    statusCodes = None
    359364
    360365    childParsers = {(NS_MUC_USER, 'x'): '_childParser_mucUser'}
    361366
    362367    def _childParser_mucUser(self, element):
     368        statusCodes = set()
     369
    363370        for child in element.elements():
    364371            if child.uri != NS_MUC_USER:
    365372                continue
     373
    366374            elif child.name == 'status':
    367                 self.statusCode = child.getAttribute('code')
    368             # TODO: item, destroy
    369 
     375                try:
     376                    statusCode = int(child.getAttribute('code'))
     377                except (TypeError, ValueError):
     378                    continue
     379
     380                statusCodes.add(statusCode)
     381
     382            elif child.name == 'item':
     383                if child.hasAttribute('jid'):
     384                    self.entity = jid.JID(child['jid'])
     385
     386                self.nick = child.getAttribute('nick')
     387                self.affiliation = child.getAttribute('affiliation')
     388                self.role = child.getAttribute('role')
     389
     390                for reason in child.elements(NS_MUC_ADMIN, 'reason'):
     391                    self.reason = unicode(reason)
     392
     393            # TODO: destroy
     394
     395        if statusCodes:
     396            self.statusCodes = statusCodes
    370397
    371398
     
    496523        query = "/presence[@from='%s' or (@from='%s' and @type='error')]" % (
    497524                stanza.recipient.full(), stanza.recipient.userhost())
    498         self.xmlstream.addOnetimeObserver(query, onResponse, priority=1)
     525        self.xmlstream.addOnetimeObserver(query, onResponse, priority=-1)
    499526        self.xmlstream.send(stanza.toElement())
    500527        return d
     
    10411068    a client.
    10421069
    1043     @ivar roomIdentifier: The Room ID of the MUC room.
    1044     @type roomIdentifier: C{unicode}
    1045 
    1046     @ivar service: The server where the MUC room is located.
    1047     @type service: C{unicode}
     1070    @ivar roomJID: The Room JID of the MUC room.
     1071    @type roomJID: L{JID}
    10481072
    10491073    @ivar nick: The nick name for the client in this room.
     
    10541078
    10551079    @ivar occupantJID: The JID of the occupant in the room. Generated from
    1056         roomIdentifier, service, and nick.
     1080        roomJID and nick.
    10571081    @type occupantJID: L{jid.JID}
    10581082    """
    10591083
    10601084
    1061     def __init__(self, roomIdentifier, service, nick, state=None):
     1085    def __init__(self, roomJID, nick, state=None):
    10621086        """
    10631087        Initialize the room.
    10641088        """
    1065         self.roomIdentifier = roomIdentifier
    1066         self.service = service
     1089        self.roomJID = roomJID
    10671090        self.setNick(nick)
    10681091        self.state = state
     
    10741097
    10751098    def setNick(self, nick):
    1076         self.occupantJID = jid.internJID(u"%s@%s/%s" % (self.roomIdentifier,
    1077                                                         self.service,
    1078                                                         nick))
     1099        self.occupantJID = jid.internJID(u"%s/%s" % (self.roomJID, nick))
    10791100        self.nick = nick
    10801101
     
    11681189
    11691190
    1170     def _removeRoom(self, occupantJID):
     1191    def _removeRoom(self, roomJID):
    11711192        """
    11721193        Delete a room from the room collection.
    11731194        """
    1174         roomJID = occupantJID.userhostJID()
    11751195        if roomJID in self._rooms:
    11761196            del self._rooms[roomJID]
    11771197
    11781198
    1179     def unavailableReceived(self, presence):
    1180         """
    1181         Unavailable presence was received.
    1182 
    1183         If this was received from a MUC room occupant JID, that occupant has
    1184         left the room.
    1185         """
    1186 
    1187         occupantJID = presence.sender
    1188 
    1189         if occupantJID:
    1190             self._userLeavesRoom(occupantJID)
    1191 
    1192 
    1193     def errorReceived(self, presence):
    1194         """
    1195         Error presence was received.
    1196 
    1197         If this was received from a MUC room occupant JID, we conclude the
    1198         occupant has left the room.
    1199         """
    1200         occupantJID = presence.sender
    1201 
    1202         if occupantJID:
    1203             self._userLeavesRoom(occupantJID)
    1204 
    1205 
    1206     def _userLeavesRoom(self, occupantJID):
     1199    def _getRoomUser(self, stanza):
     1200        """
     1201        Lookup the room and user associated with the stanza's sender.
     1202        """
     1203        occupantJID = stanza.sender
     1204
     1205        if not occupantJID:
     1206            return None, None
     1207
    12071208        # when a user leaves a room we need to update it
    12081209        room = self._getRoom(occupantJID.userhostJID())
    12091210        if room is None:
    12101211            # not in the room yet
     1212            return None, None
     1213
     1214        # Check if user is in roster
     1215        nick = occupantJID.resource
     1216        user = room.getUser(nick)
     1217
     1218        return room, user
     1219
     1220
     1221    def unavailableReceived(self, presence):
     1222        """
     1223        Unavailable presence was received.
     1224
     1225        If this was received from a MUC room occupant JID, that occupant has
     1226        left the room.
     1227        """
     1228
     1229        room, user = self._getRoomUser(presence)
     1230
     1231        if room is None or user is None:
    12111232            return
    1212         # check if user is in roster
    1213         user = room.getUser(occupantJID.resource)
     1233
     1234        room.removeUser(user)
     1235        self.userLeftRoom(room, user)
     1236
     1237
     1238    def availableReceived(self, presence):
     1239        """
     1240        Available presence was received.
     1241        """
     1242
     1243        room, user = self._getRoomUser(presence)
     1244
     1245        if room is None:
     1246            return
     1247
    12141248        if user is None:
    1215             return
     1249            nick = presence.sender.resource
     1250            user = User(nick, presence.entity)
     1251
     1252        # Update user status
     1253        user.status = presence.status
     1254        user.show = presence.show
     1255
    12161256        if room.inRoster(user):
    1217             room.removeUser(user)
    1218             self.userLeftRoom(room, user)
    1219 
    1220 
    1221     def availableReceived(self, presence):
    1222         """
    1223         Available presence was received.
    1224         """
    1225 
    1226         occupantJID = presence.sender
    1227 
    1228         if not occupantJID:
    1229             return
    1230 
    1231         # grab room
    1232         room = self._getRoom(occupantJID.userhostJID())
    1233         if room is None:
    1234             # not in the room yet
    1235             return
    1236 
    1237         user = self._changeUserStatus(room, occupantJID, presence.status,
    1238                                       presence.show)
    1239 
    1240         if room.inRoster(user):
    1241             # we changed status or nick
    1242             if presence.statusCode:
    1243                 room.status = presence.statusCode # XXX
    1244             else:
    1245                 self.userUpdatedStatus(room, user, presence.show,
    1246                                        presence.status)
     1257            self.userUpdatedStatus(room, user, presence.show, presence.status)
    12471258        else:
    12481259            room.addUser(user)
     
    12571268        L{receivedGroupChat}, L{receivedHistory} or L{receivedHistory}.
    12581269        """
    1259         occupantJID = message.sender
    1260         if not occupantJID:
     1270        room, user = self._getRoomUser(message)
     1271
     1272        if room is None:
    12611273            return
    1262 
    1263         roomJID = occupantJID.userhostJID()
    1264 
    1265         room = self._getRoom(roomJID)
    1266         if room is None:
    1267             # not in the room yet
    1268             return
    1269 
    1270         if occupantJID.resource:
    1271             user = room.getUser(occupantJID.resource)
    1272         else:
    1273             # This message is from the room itself.
    1274             user = None
    12751274
    12761275        if message.subject:
     
    12821281
    12831282
    1284     def _joinedRoom(self, presence):
    1285         """
    1286         We have presence that says we joined a room.
    1287         """
    1288         roomJID = presence.sender.userhostJID()
    1289 
    1290         # change the state of the room
    1291         room = self._getRoom(roomJID)
    1292         room.state = 'joined'
    1293 
    1294         # grab status
    1295         if presence.statusCode:
    1296             room.status = presence.statusCode
    1297 
    1298         return room
    1299 
    1300 
    1301     def _leftRoom(self, presence):
    1302         """
    1303         We have presence that says we left a room.
    1304         """
    1305         occupantJID = presence.sender
    1306 
    1307         # change the state of the room
    1308         self._removeRoom(occupantJID)
    1309 
    1310         return True
    1311 
    1312 
    13131283    def userJoinedRoom(self, room, user):
    13141284        """
     
    13531323
    13541324
    1355     def receivedSubject(self, room, subject):
    1356         """
     1325    def receivedSubject(self, room, user, subject):
     1326        """
     1327        A (new) room subject has been received.
     1328
    13571329        This method will need to be modified inorder for clients to
    13581330        do something when this event occurs.
     
    14011373    def join(self, roomJID, nick, historyOptions=None,
    14021374                   password=None):
    1403         room = Room(roomJID.user, roomJID.host, nick, state='joining')
     1375        """
     1376        Join a MUC room by sending presence to it.
     1377
     1378        @param roomJID: The JID of the room the entity is joining.
     1379        @type roomJID: L{jid.JID}
     1380
     1381        @param nick: The nick name for the entitity joining the room.
     1382        @type nick: C{unicode}
     1383
     1384        @param historyOptions: Options for conversation history sent by the
     1385            room upon joining.
     1386        @type historyOptions: L{HistoryOptions}
     1387
     1388        @param password: Optional password for the room.
     1389        @type password: C{unicode}
     1390
     1391        @return: A deferred that fires with the room when the entity is in the
     1392            room, or with a failure if an error has occurred.
     1393        """
     1394        def cb(presence):
     1395            """
     1396            We have presence that says we joined a room.
     1397            """
     1398            room.state = 'joined'
     1399            return room
     1400
     1401        def eb(failure):
     1402            self._removeRoom(roomJID)
     1403            return failure
     1404
     1405        room = Room(roomJID, nick, state='joining')
    14041406        self._addRoom(room)
    14051407
    14061408        d = MUCClientProtocol.join(self, roomJID, nick, historyOptions,
    14071409                                         password)
    1408         d.addCallback(self._joinedRoom)
     1410        d.addCallbacks(cb, eb)
    14091411        return d
    1410 
    1411 
    1412     def _changeUserStatus(self, room, occupantJID, status, show):
    1413         """
    1414         Change the user status in a room.
    1415         """
    1416 
    1417         # check if user is in roster
    1418         user = room.getUser(occupantJID.resource)
    1419         if user is None: # create a user that does not exist
    1420             user = User(occupantJID.resource)
    1421 
    1422         if status is not None:
    1423             user.status = unicode(status)
    1424         if show is not None:
    1425             user.show = unicode(show)
    1426 
    1427         return user
    1428 
    1429 
    1430     def _changed(self, presence, occupantJID):
    1431         """
    1432         Callback for changing the nick and status.
    1433         """
    1434         room = self._getRoom(occupantJID.userhostJID())
    1435         self._changeUserStatus(room, occupantJID, presence.status, presence.show)
    1436 
    1437         return room
    14381412
    14391413
     
    14501424        @type nick: C{unicode}
    14511425        """
     1426        def cb(presence):
     1427            # Presence confirmation, change the nickname.
     1428            room.setNick(nick)
     1429            return room
     1430
    14521431        room = self._getRoom(roomJID)
    14531432
    1454         # Change the nickname
    1455         room.setNick(nick)
    1456 
    14571433        d = MUCClientProtocol.nick(self, roomJID, nick)
    1458         d.addCallback(self._changed, room.occupantJID)
     1434        d.addCallback(cb)
    14591435        return d
    14601436
     
    14691445        @type roomJID: L{jid.JID}
    14701446        """
     1447        def cb(presence):
     1448            self._removeRoom(roomJID)
     1449
    14711450        d = MUCClientProtocol.leave(self, roomJID)
    1472         d.addCallback(self._leftRoom)
     1451        d.addCallback(cb)
    14731452        return d
    14741453
     
    14921471        room = self._getRoom(roomJID)
    14931472        d = MUCClientProtocol.status(self, roomJID, show, status)
    1494         d.addCallback(self._changed, room.occupantJID)
     1473        d.addCallback(lambda _: room)
    14951474        return d
    14961475
Note: See TracChangeset for help on using the changeset viewer.