Package pyxmpp :: Package jabber :: Module muc
[hide private]

Source Code for Module pyxmpp.jabber.muc

   1  # 
   2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net> 
   3  # 
   4  # This program is free software; you can redistribute it and/or modify 
   5  # it under the terms of the GNU Lesser General Public License Version 
   6  # 2.1 as published by the Free Software Foundation. 
   7  # 
   8  # This program is distributed in the hope that it will be useful, 
   9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  11  # GNU Lesser General Public License for more details. 
  12  # 
  13  # You should have received a copy of the GNU Lesser General Public 
  14  # License along with this program; if not, write to the Free Software 
  15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  16  # 
  17  """Jabber Multi-User Chat implementation. 
  18   
  19  Normative reference: 
  20    - `JEP 45 <http://www.jabber.org/jeps/jep-0045.html>`__ 
  21  """ 
  22   
  23  __revision__="$Id: muc.py 714 2010-04-05 10:20:10Z jajcus $" 
  24  __docformat__="restructuredtext en" 
  25   
  26  import logging 
  27   
  28  from pyxmpp.presence import Presence 
  29  from pyxmpp.message import Message 
  30  from pyxmpp.iq import Iq 
  31  from pyxmpp.jid import JID 
  32   
  33  from pyxmpp.xmlextra import xml_element_ns_iter 
  34   
  35  from pyxmpp.jabber.muccore import MucPresence,MucUserX,MucItem,MucStatus 
  36  from pyxmpp.jabber.muccore import MUC_OWNER_NS 
  37   
  38  from pyxmpp.jabber.dataforms import DATAFORM_NS, Form 
  39   
  40  import weakref 
  41   
42 -class MucRoomHandler:
43 """ 44 Base class for MUC room handlers. 45 46 Methods of this class will be called for various events in the room. 47 48 :Ivariables: 49 - `room_state`: MucRoomState object describing room state and its 50 participants. 51 52 """
53 - def __init__(self):
54 """Initialize a `MucRoomHandler` object.""" 55 self.room_state=None 56 self.__logger=logging.getLogger("pyxmpp.jabber.MucRoomHandler")
57
58 - def assign_state(self,state_obj):
59 """Assign a state object to this `MucRoomHandler` instance. 60 61 :Parameters: 62 - `state_obj`: the state object. 63 :Types: 64 - `state_obj`: `MucRoomState`""" 65 self.room_state=state_obj
66
67 - def room_created(self, stanza):
68 """ 69 Called when the room has been created. 70 71 Default action is to request an "instant room" by accepting the default 72 configuration. Instead the application may want to request a 73 configuration form and submit it. 74 75 :Parameters: 76 - `stanza`: the stanza received. 77 78 :Types: 79 - `stanza`: `pyxmpp.stanza.Stanza` 80 """ 81 _unused = stanza 82 self.room_state.request_instant_room()
83
84 - def configuration_form_received(self,form):
85 """ 86 Called when a requested configuration form is received. 87 88 The form, after filling-in shoul be passed to `self.room_state.configure_room`. 89 90 :Parameters: 91 - `form`: the configuration form. 92 93 :Types: 94 - `form`: `pyxmpp.jabber.dataforms.Form` 95 """ 96 pass
97
98 - def room_configured(self):
99 """ 100 Called after a successfull room configuration. 101 """ 102 pass
103
104 - def user_joined(self,user,stanza):
105 """ 106 Called when a new participant joins the room. 107 108 :Parameters: 109 - `user`: the user joining. 110 - `stanza`: the stanza received. 111 112 :Types: 113 - `user`: `MucRoomUser` 114 - `stanza`: `pyxmpp.stanza.Stanza` 115 """ 116 pass
117
118 - def user_left(self,user,stanza):
119 """ 120 Called when a participant leaves the room. 121 122 :Parameters: 123 - `user`: the user leaving. 124 - `stanza`: the stanza received. 125 126 :Types: 127 - `user`: `MucRoomUser` 128 - `stanza`: `pyxmpp.stanza.Stanza` 129 """ 130 pass
131
132 - def role_changed(self,user,old_role,new_role,stanza):
133 """ 134 Called when a role of an user has been changed. 135 136 :Parameters: 137 - `user`: the user (after update). 138 - `old_role`: user's role before update. 139 - `new_role`: user's role after update. 140 - `stanza`: the stanza received. 141 142 :Types: 143 - `user`: `MucRoomUser` 144 - `old_role`: `unicode` 145 - `new_role`: `unicode` 146 - `stanza`: `pyxmpp.stanza.Stanza` 147 """ 148 pass
149
150 - def affiliation_changed(self,user,old_aff,new_aff,stanza):
151 """ 152 Called when a affiliation of an user has been changed. 153 154 `user` MucRoomUser object describing the user (after update). 155 `old_aff` is user's affiliation before update. 156 `new_aff` is user's affiliation after update. 157 `stanza` the stanza received. 158 """ 159 pass
160
161 - def nick_change(self,user,new_nick,stanza):
162 """ 163 Called when user nick change is started. 164 165 :Parameters: 166 - `user`: the user (before update). 167 - `new_nick`: the new nick. 168 - `stanza`: the stanza received. 169 170 :Types: 171 - `user`: `MucRoomUser` 172 - `new_nick`: `unicode` 173 - `stanza`: `pyxmpp.stanza.Stanza` 174 """ 175 pass
176
177 - def nick_changed(self,user,old_nick,stanza):
178 """ 179 Called after a user nick has been changed. 180 181 :Parameters: 182 - `user`: the user (after update). 183 - `old_nick`: the old nick. 184 - `stanza`: the stanza received. 185 186 :Types: 187 - `user`: `MucRoomUser` 188 - `old_nick`: `unicode` 189 - `stanza`: `pyxmpp.stanza.Stanza` 190 """ 191 pass
192
193 - def presence_changed(self,user,stanza):
194 """ 195 Called whenever user's presence changes (includes nick, role or 196 affiliation changes). 197 198 :Parameters: 199 - `user`: MucRoomUser object describing the user. 200 - `stanza`: the stanza received. 201 202 :Types: 203 - `user`: `MucRoomUser` 204 - `stanza`: `pyxmpp.stanza.Stanza` 205 """ 206 pass
207
208 - def subject_changed(self,user,stanza):
209 """ 210 Called when the room subject has been changed. 211 212 :Parameters: 213 - `user`: the user changing the subject. 214 - `stanza`: the stanza used to change the subject. 215 216 :Types: 217 - `user`: `MucRoomUser` 218 - `stanza`: `pyxmpp.stanza.Stanza` 219 """ 220 pass
221
222 - def message_received(self,user,stanza):
223 """ 224 Called when groupchat message has been received. 225 226 :Parameters: 227 - `user`: the sender. 228 - `stanza`: is the message stanza received. 229 230 :Types: 231 - `user`: `MucRoomUser` 232 - `stanza`: `pyxmpp.stanza.Stanza` 233 """ 234 pass
235
236 - def room_configuration_error(self,stanza):
237 """ 238 Called when an error stanza is received in reply to a room 239 configuration request. 240 241 By default `self.error` is called. 242 243 :Parameters: 244 - `stanza`: the stanza received. 245 :Types: 246 - `stanza`: `pyxmpp.stanza.Stanza` 247 """ 248 self.error(stanza)
249
250 - def error(self,stanza):
251 """ 252 Called when an error stanza is received. 253 254 :Parameters: 255 - `stanza`: the stanza received. 256 :Types: 257 - `stanza`: `pyxmpp.stanza.Stanza` 258 """ 259 err=stanza.get_error() 260 self.__logger.debug("Error from: %r Condition: %r" 261 % (stanza.get_from(),err.get_condition))
262
263 -class MucRoomUser:
264 """ 265 Describes a user of a MUC room. 266 267 The attributes of this object should not be changed directly. 268 269 :Ivariables: 270 - `presence`: last presence stanza received for the user. 271 - `role`: user's role. 272 - `affiliation`: user's affiliation. 273 - `room_jid`: user's room jid. 274 - `real_jid`: user's real jid or None if not available. 275 - `nick`: user's nick (resource part of `room_jid`) 276 :Types: 277 - `presence`: `MucPresence` 278 - `role`: `str` 279 - `affiliation`: `str` 280 - `room_jid`: `JID` 281 - `real_jid`: `JID` 282 - `nick`: `unicode` 283 """
284 - def __init__(self,presence_or_user_or_jid):
285 """ 286 Initialize a `MucRoomUser` object. 287 288 :Parameters: 289 - `presence_or_user_or_jid`: a MUC presence stanza with user 290 information, a user object to copy or a room JID of a user. 291 :Types: 292 - `presence_or_user_or_jid`: `MucPresence` or `MucRoomUser` or 293 `JID` 294 295 When `presence_or_user_or_jid` is a JID user's 296 role and affiliation are set to "none". 297 """ 298 if isinstance(presence_or_user_or_jid,MucRoomUser): 299 self.presence=presence_or_user_or_jid.presence 300 self.role=presence_or_user_or_jid.role 301 self.affiliation=presence_or_user_or_jid.affiliation 302 self.room_jid=presence_or_user_or_jid.room_jid 303 self.real_jid=presence_or_user_or_jid.real_jid 304 self.nick=presence_or_user_or_jid.nick 305 self.new_nick=None 306 else: 307 self.affiliation="none" 308 self.presence=None 309 self.real_jid=None 310 self.new_nick=None 311 if isinstance(presence_or_user_or_jid,JID): 312 self.nick=presence_or_user_or_jid.resource 313 self.room_jid=presence_or_user_or_jid 314 self.role="none" 315 elif isinstance(presence_or_user_or_jid,Presence): 316 self.nick=None 317 self.room_jid=None 318 self.role="participant" 319 self.update_presence(presence_or_user_or_jid) 320 else: 321 raise TypeError,"Bad argument type for MucRoomUser constructor"
322
323 - def update_presence(self,presence):
324 """ 325 Update user information. 326 327 :Parameters: 328 - `presence`: a presence stanza with user information update. 329 :Types: 330 - `presence`: `MucPresence` 331 """ 332 self.presence=MucPresence(presence) 333 t=presence.get_type() 334 if t=="unavailable": 335 self.role="none" 336 self.affiliation="none" 337 self.room_jid=self.presence.get_from() 338 self.nick=self.room_jid.resource 339 mc=self.presence.get_muc_child() 340 if isinstance(mc,MucUserX): 341 items=mc.get_items() 342 for item in items: 343 if not isinstance(item,MucItem): 344 continue 345 if item.role: 346 self.role=item.role 347 if item.affiliation: 348 self.affiliation=item.affiliation 349 if item.jid: 350 self.real_jid=item.jid 351 if item.nick: 352 self.new_nick=item.nick 353 break
354
355 - def same_as(self,other):
356 """Check if two `MucRoomUser` objects describe the same user in the 357 same room. 358 359 :Parameters: 360 - `other`: the user object to compare `self` with. 361 :Types: 362 - `other`: `MucRoomUser` 363 364 :return: `True` if the two object describe the same user. 365 :returntype: `bool`""" 366 return self.room_jid==other.room_jid
367
368 -class MucRoomState:
369 """ 370 Describes the state of a MUC room, handles room events 371 and provides an interface for room actions. 372 373 :Ivariables: 374 - `own_jid`: real jid of the owner (client using this class). 375 - `room_jid`: room jid of the owner. 376 - `handler`: MucRoomHandler object containing callbacks to be called. 377 - `manager`: MucRoomManager object managing this room. 378 - `joined`: True if the channel is joined. 379 - `subject`: current subject of the room. 380 - `users`: dictionary of users in the room. Nicknames are the keys. 381 - `me`: MucRoomUser instance of the owner. 382 - `configured`: `False` if the room requires configuration. 383 """
384 - def __init__(self,manager,own_jid,room_jid,handler):
385 """ 386 Initialize a `MucRoomState` object. 387 388 :Parameters: 389 - `manager`: an object to manage this room. 390 - `own_jid`: real JID of the owner (client using this class). 391 - `room_jid`: room JID of the owner (provides the room name and 392 the nickname). 393 - `handler`: an object to handle room events. 394 :Types: 395 - `manager`: `MucRoomManager` 396 - `own_jid`: JID 397 - `room_jid`: JID 398 - `handler`: `MucRoomHandler` 399 """ 400 self.own_jid=own_jid 401 self.room_jid=room_jid 402 self.handler=handler 403 self.manager=weakref.proxy(manager) 404 self.joined=False 405 self.subject=None 406 self.users={} 407 self.me=MucRoomUser(room_jid) 408 self.configured = None 409 self.configuration_form = None 410 handler.assign_state(self) 411 self.__logger=logging.getLogger("pyxmpp.jabber.MucRoomState")
412
413 - def get_user(self,nick_or_jid,create=False):
414 """ 415 Get a room user with given nick or JID. 416 417 :Parameters: 418 - `nick_or_jid`: the nickname or room JID of the user requested. 419 - `create`: if `True` and `nick_or_jid` is a JID, then a new 420 user object will be created if there is no such user in the room. 421 :Types: 422 - `nick_or_jid`: `unicode` or `JID` 423 - `create`: `bool` 424 425 :return: the named user or `None` 426 :returntype: `MucRoomUser` 427 """ 428 if isinstance(nick_or_jid,JID): 429 if not nick_or_jid.resource: 430 return None 431 for u in self.users.values(): 432 if nick_or_jid in (u.room_jid,u.real_jid): 433 return u 434 if create: 435 return MucRoomUser(nick_or_jid) 436 else: 437 return None 438 return self.users.get(nick_or_jid)
439
440 - def set_stream(self,stream):
441 """ 442 Called when current stream changes. 443 444 Mark the room not joined and inform `self.handler` that it was left. 445 446 :Parameters: 447 - `stream`: the new stream. 448 :Types: 449 - `stream`: `pyxmpp.stream.Stream` 450 """ 451 _unused = stream 452 if self.joined and self.handler: 453 self.handler.user_left(self.me,None) 454 self.joined=False
455
456 - def join(self, password=None, history_maxchars = None, 457 history_maxstanzas = None, history_seconds = None, history_since = None):
458 """ 459 Send a join request for the room. 460 461 :Parameters: 462 - `password`: password to the room. 463 - `history_maxchars`: limit of the total number of characters in 464 history. 465 - `history_maxstanzas`: limit of the total number of messages in 466 history. 467 - `history_seconds`: send only messages received in the last 468 `history_seconds` seconds. 469 - `history_since`: Send only the messages received since the 470 dateTime specified (UTC). 471 :Types: 472 - `password`: `unicode` 473 - `history_maxchars`: `int` 474 - `history_maxstanzas`: `int` 475 - `history_seconds`: `int` 476 - `history_since`: `datetime.datetime` 477 """ 478 if self.joined: 479 raise RuntimeError,"Room is already joined" 480 p=MucPresence(to_jid=self.room_jid) 481 p.make_join_request(password, history_maxchars, history_maxstanzas, 482 history_seconds, history_since) 483 self.manager.stream.send(p)
484
485 - def leave(self):
486 """ 487 Send a leave request for the room. 488 """ 489 if self.joined: 490 p=MucPresence(to_jid=self.room_jid,stanza_type="unavailable") 491 self.manager.stream.send(p)
492
493 - def send_message(self,body):
494 """ 495 Send a message to the room. 496 497 :Parameters: 498 - `body`: the message body. 499 :Types: 500 - `body`: `unicode` 501 """ 502 m=Message(to_jid=self.room_jid.bare(),stanza_type="groupchat",body=body) 503 self.manager.stream.send(m)
504
505 - def set_subject(self,subject):
506 """ 507 Send a subject change request to the room. 508 509 :Parameters: 510 - `subject`: the new subject. 511 :Types: 512 - `subject`: `unicode` 513 """ 514 m=Message(to_jid=self.room_jid.bare(),stanza_type="groupchat",subject=subject) 515 self.manager.stream.send(m)
516
517 - def change_nick(self,new_nick):
518 """ 519 Send a nick change request to the room. 520 521 :Parameters: 522 - `new_nick`: the new nickname requested. 523 :Types: 524 - `new_nick`: `unicode` 525 """ 526 new_room_jid=JID(self.room_jid.node,self.room_jid.domain,new_nick) 527 p=Presence(to_jid=new_room_jid) 528 self.manager.stream.send(p)
529
530 - def get_room_jid(self,nick=None):
531 """ 532 Get own room JID or a room JID for given `nick`. 533 534 :Parameters: 535 - `nick`: a nick for which the room JID is requested. 536 :Types: 537 - `nick`: `unicode` 538 539 :return: the room JID. 540 :returntype: `JID` 541 """ 542 if nick is None: 543 return self.room_jid 544 return JID(self.room_jid.node,self.room_jid.domain,nick)
545
546 - def get_nick(self):
547 """ 548 Get own nick. 549 550 :return: own nick. 551 :returntype: `unicode` 552 """ 553 return self.room_jid.resource
554
555 - def process_available_presence(self,stanza):
556 """ 557 Process <presence/> received from the room. 558 559 :Parameters: 560 - `stanza`: the stanza received. 561 :Types: 562 - `stanza`: `MucPresence` 563 """ 564 fr=stanza.get_from() 565 if not fr.resource: 566 return 567 nick=fr.resource 568 user=self.users.get(nick) 569 if user: 570 old_user=MucRoomUser(user) 571 user.update_presence(stanza) 572 user.nick=nick 573 else: 574 old_user=None 575 user=MucRoomUser(stanza) 576 self.users[user.nick]=user 577 self.handler.presence_changed(user,stanza) 578 if fr==self.room_jid and not self.joined: 579 self.joined=True 580 self.me=user 581 mc=stanza.get_muc_child() 582 if isinstance(mc,MucUserX): 583 status = [i for i in mc.get_items() if isinstance(i,MucStatus) and i.code==201] 584 if status: 585 self.configured = False 586 self.handler.room_created(stanza) 587 if self.configured is None: 588 self.configured = True 589 if not old_user or old_user.role=="none": 590 self.handler.user_joined(user,stanza) 591 else: 592 if old_user.nick!=user.nick: 593 self.handler.nick_changed(user,old_user.nick,stanza) 594 if old_user.room_jid==self.room_jid: 595 self.room_jid=fr 596 if old_user.role!=user.role: 597 self.handler.role_changed(user,old_user.role,user.role,stanza) 598 if old_user.affiliation!=user.affiliation: 599 self.handler.affiliation_changed(user,old_user.affiliation,user.affiliation,stanza)
600
601 - def process_unavailable_presence(self,stanza):
602 """ 603 Process <presence type="unavailable"/> received from the room. 604 605 :Parameters: 606 - `stanza`: the stanza received. 607 :Types: 608 - `stanza`: `MucPresence` 609 """ 610 fr=stanza.get_from() 611 if not fr.resource: 612 return 613 nick=fr.resource 614 user=self.users.get(nick) 615 if user: 616 old_user=MucRoomUser(user) 617 user.update_presence(stanza) 618 self.handler.presence_changed(user,stanza) 619 if user.new_nick: 620 mc=stanza.get_muc_child() 621 if isinstance(mc,MucUserX): 622 renames=[i for i in mc.get_items() if isinstance(i,MucStatus) and i.code==303] 623 if renames: 624 self.users[user.new_nick]=user 625 del self.users[nick] 626 return 627 else: 628 old_user=None 629 user=MucRoomUser(stanza) 630 self.users[user.nick]=user 631 self.handler.presence_changed(user,stanza) 632 if fr==self.room_jid and self.joined: 633 self.joined=False 634 self.handler.user_left(user,stanza) 635 self.manager.forget(self) 636 self.me=user 637 elif old_user: 638 self.handler.user_left(user,stanza)
639 # TODO: kicks 640
641 - def process_groupchat_message(self,stanza):
642 """ 643 Process <message type="groupchat"/> received from the room. 644 645 :Parameters: 646 - `stanza`: the stanza received. 647 :Types: 648 - `stanza`: `Message` 649 """ 650 fr=stanza.get_from() 651 user=self.get_user(fr,True) 652 s=stanza.get_subject() 653 if s: 654 self.subject=s 655 self.handler.subject_changed(user,stanza) 656 else: 657 self.handler.message_received(user,stanza)
658
659 - def process_error_message(self,stanza):
660 """ 661 Process <message type="error"/> received from the room. 662 663 :Parameters: 664 - `stanza`: the stanza received. 665 :Types: 666 - `stanza`: `Message` 667 """ 668 self.handler.error(stanza)
669
670 - def process_error_presence(self,stanza):
671 """ 672 Process <presence type="error"/> received from the room. 673 674 :Parameters: 675 - `stanza`: the stanza received. 676 :Types: 677 - `stanza`: `Presence` 678 """ 679 self.handler.error(stanza)
680
681 - def process_configuration_form_success(self, stanza):
682 """ 683 Process successful result of a room configuration form request. 684 685 :Parameters: 686 - `stanza`: the stanza received. 687 :Types: 688 - `stanza`: `Presence` 689 """ 690 if stanza.get_query_ns() != MUC_OWNER_NS: 691 raise ValueError, "Bad result namespace" # TODO: ProtocolError 692 query = stanza.get_query() 693 form = None 694 for el in xml_element_ns_iter(query.children, DATAFORM_NS): 695 form = Form(el) 696 break 697 if not form: 698 raise ValueError, "No form received" # TODO: ProtocolError 699 self.configuration_form = form 700 self.handler.configuration_form_received(form)
701
702 - def process_configuration_form_error(self, stanza):
703 """ 704 Process error response for a room configuration form request. 705 706 :Parameters: 707 - `stanza`: the stanza received. 708 :Types: 709 - `stanza`: `Presence` 710 """ 711 self.handler.error(stanza)
712
713 - def request_configuration_form(self):
714 """ 715 Request a configuration form for the room. 716 717 When the form is received `self.handler.configuration_form_received` will be called. 718 When an error response is received then `self.handler.error` will be called. 719 720 :return: id of the request stanza. 721 :returntype: `unicode` 722 """ 723 iq = Iq(to_jid = self.room_jid.bare(), stanza_type = "get") 724 iq.new_query(MUC_OWNER_NS, "query") 725 self.manager.stream.set_response_handlers( 726 iq, self.process_configuration_form_success, self.process_configuration_form_error) 727 self.manager.stream.send(iq) 728 return iq.get_id()
729
730 - def process_configuration_success(self, stanza):
731 """ 732 Process success response for a room configuration request. 733 734 :Parameters: 735 - `stanza`: the stanza received. 736 :Types: 737 - `stanza`: `Presence` 738 """ 739 _unused = stanza 740 self.configured = True 741 self.handler.room_configured()
742
743 - def process_configuration_error(self, stanza):
744 """ 745 Process error response for a room configuration request. 746 747 :Parameters: 748 - `stanza`: the stanza received. 749 :Types: 750 - `stanza`: `Presence` 751 """ 752 self.handler.room_configuration_error(stanza)
753
754 - def configure_room(self, form):
755 """ 756 Configure the room using the provided data. 757 Do nothing if the provided form is of type 'cancel'. 758 759 :Parameters: 760 - `form`: the configuration parameters. Should be a 'submit' form made by filling-in 761 the configuration form retireved using `self.request_configuration_form` or 762 a 'cancel' form. 763 :Types: 764 - `form`: `Form` 765 766 :return: id of the request stanza or `None` if a 'cancel' form was provieded. 767 :returntype: `unicode` 768 """ 769 770 if form.type == "cancel": 771 return None 772 elif form.type != "submit": 773 raise ValueError, "A 'submit' form required to configure a room" 774 iq = Iq(to_jid = self.room_jid.bare(), stanza_type = "set") 775 query = iq.new_query(MUC_OWNER_NS, "query") 776 form.as_xml(query) 777 self.manager.stream.set_response_handlers( 778 iq, self.process_configuration_success, self.process_configuration_error) 779 self.manager.stream.send(iq) 780 return iq.get_id()
781
782 - def request_instant_room(self):
783 """ 784 Request an "instant room" -- the default configuration for a MUC room. 785 786 :return: id of the request stanza. 787 :returntype: `unicode` 788 """ 789 if self.configured: 790 raise RuntimeError, "Instant room may be requested for unconfigured room only" 791 form = Form("submit") 792 return self.configure_room(form)
793
794 -class MucRoomManager:
795 """ 796 Manage collection of MucRoomState objects and dispatch events. 797 798 :Ivariables: 799 - `rooms`: a dictionary containing known MUC rooms. Unicode room JIDs are the 800 keys. 801 - `stream`: the stream associated with the room manager. 802 803 """
804 - def __init__(self,stream):
805 """ 806 Initialize a `MucRoomManager` object. 807 808 :Parameters: 809 - `stream`: a stream to be initially assigned to `self`. 810 :Types: 811 - `stream`: `pyxmpp.stream.Stream` 812 """ 813 self.rooms={} 814 self.stream,self.jid=(None,)*2 815 self.set_stream(stream) 816 self.__logger=logging.getLogger("pyxmpp.jabber.MucRoomManager")
817
818 - def set_stream(self,stream):
819 """ 820 Change the stream assigned to `self`. 821 822 :Parameters: 823 - `stream`: the new stream to be assigned to `self`. 824 :Types: 825 - `stream`: `pyxmpp.stream.Stream` 826 """ 827 self.jid=stream.me 828 self.stream=stream 829 for r in self.rooms.values(): 830 r.set_stream(stream)
831
832 - def set_handlers(self,priority=10):
833 """ 834 Assign MUC stanza handlers to the `self.stream`. 835 836 :Parameters: 837 - `priority`: priority for the handlers. 838 :Types: 839 - `priority`: `int` 840 """ 841 self.stream.set_message_handler("groupchat",self.__groupchat_message,None,priority) 842 self.stream.set_message_handler("error",self.__error_message,None,priority) 843 self.stream.set_presence_handler("available",self.__presence_available,None,priority) 844 self.stream.set_presence_handler("unavailable",self.__presence_unavailable,None,priority) 845 self.stream.set_presence_handler("error",self.__presence_error,None,priority)
846
847 - def join(self, room, nick, handler, password = None, history_maxchars = None, 848 history_maxstanzas = None, history_seconds = None, history_since = None):
849 """ 850 Create and return a new room state object and request joining 851 to a MUC room. 852 853 :Parameters: 854 - `room`: the name of a room to be joined 855 - `nick`: the nickname to be used in the room 856 - `handler`: is an object to handle room events. 857 - `password`: password for the room, if any 858 - `history_maxchars`: limit of the total number of characters in 859 history. 860 - `history_maxstanzas`: limit of the total number of messages in 861 history. 862 - `history_seconds`: send only messages received in the last 863 `history_seconds` seconds. 864 - `history_since`: Send only the messages received since the 865 dateTime specified (UTC). 866 867 :Types: 868 - `room`: `JID` 869 - `nick`: `unicode` 870 - `handler`: `MucRoomHandler` 871 - `password`: `unicode` 872 - `history_maxchars`: `int` 873 - `history_maxstanzas`: `int` 874 - `history_seconds`: `int` 875 - `history_since`: `datetime.datetime` 876 877 :return: the room state object created. 878 :returntype: `MucRoomState` 879 """ 880 881 if not room.node or room.resource: 882 raise ValueError,"Invalid room JID" 883 884 room_jid = JID(room.node, room.domain, nick) 885 886 cur_rs = self.rooms.get(room_jid.bare().as_unicode()) 887 if cur_rs and cur_rs.joined: 888 raise RuntimeError,"Room already joined" 889 890 rs=MucRoomState(self, self.stream.me, room_jid, handler) 891 self.rooms[room_jid.bare().as_unicode()]=rs 892 rs.join(password, history_maxchars, history_maxstanzas, 893 history_seconds, history_since) 894 return rs
895
896 - def get_room_state(self,room):
897 """Get the room state object of a room. 898 899 :Parameters: 900 - `room`: JID or the room which state is requested. 901 :Types: 902 - `room`: `JID` 903 904 :return: the state object. 905 :returntype: `MucRoomState`""" 906 return self.rooms.get(room.bare().as_unicode())
907
908 - def forget(self,rs):
909 """ 910 Remove a room from the list of managed rooms. 911 912 :Parameters: 913 - `rs`: the state object of the room. 914 :Types: 915 - `rs`: `MucRoomState` 916 """ 917 try: 918 del self.rooms[rs.room_jid.bare().as_unicode()] 919 except KeyError: 920 pass
921
922 - def __groupchat_message(self,stanza):
923 """Process a groupchat message from a MUC room. 924 925 :Parameters: 926 - `stanza`: the stanza received. 927 :Types: 928 - `stanza`: `Message` 929 930 :return: `True` if the message was properly recognized as directed to 931 one of the managed rooms, `False` otherwise. 932 :returntype: `bool`""" 933 fr=stanza.get_from() 934 key=fr.bare().as_unicode() 935 rs=self.rooms.get(key) 936 if not rs: 937 self.__logger.debug("groupchat message from unknown source") 938 return False 939 rs.process_groupchat_message(stanza) 940 return True
941
942 - def __error_message(self,stanza):
943 """Process an error message from a MUC room. 944 945 :Parameters: 946 - `stanza`: the stanza received. 947 :Types: 948 - `stanza`: `Message` 949 950 :return: `True` if the message was properly recognized as directed to 951 one of the managed rooms, `False` otherwise. 952 :returntype: `bool`""" 953 fr=stanza.get_from() 954 key=fr.bare().as_unicode() 955 rs=self.rooms.get(key) 956 if not rs: 957 return False 958 rs.process_error_message(stanza) 959 return True
960
961 - def __presence_error(self,stanza):
962 """Process an presence error from a MUC room. 963 964 :Parameters: 965 - `stanza`: the stanza received. 966 :Types: 967 - `stanza`: `Presence` 968 969 :return: `True` if the stanza was properly recognized as generated by 970 one of the managed rooms, `False` otherwise. 971 :returntype: `bool`""" 972 fr=stanza.get_from() 973 key=fr.bare().as_unicode() 974 rs=self.rooms.get(key) 975 if not rs: 976 return False 977 rs.process_error_presence(stanza) 978 return True
979
980 - def __presence_available(self,stanza):
981 """Process an available presence from a MUC room. 982 983 :Parameters: 984 - `stanza`: the stanza received. 985 :Types: 986 - `stanza`: `Presence` 987 988 :return: `True` if the stanza was properly recognized as generated by 989 one of the managed rooms, `False` otherwise. 990 :returntype: `bool`""" 991 fr=stanza.get_from() 992 key=fr.bare().as_unicode() 993 rs=self.rooms.get(key) 994 if not rs: 995 return False 996 rs.process_available_presence(MucPresence(stanza)) 997 return True
998
999 - def __presence_unavailable(self,stanza):
1000 """Process an unavailable presence from a MUC room. 1001 1002 :Parameters: 1003 - `stanza`: the stanza received. 1004 :Types: 1005 - `stanza`: `Presence` 1006 1007 :return: `True` if the stanza was properly recognized as generated by 1008 one of the managed rooms, `False` otherwise. 1009 :returntype: `bool`""" 1010 fr=stanza.get_from() 1011 key=fr.bare().as_unicode() 1012 rs=self.rooms.get(key) 1013 if not rs: 1014 return False 1015 rs.process_unavailable_presence(MucPresence(stanza)) 1016 return True
1017 1018 # vi: sts=4 et sw=4 1019