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