comparison src/plugins/plugin_xep_0045.py @ 1082:246712d2e7bc

plugin XEP-0045, text_commands: add some commands: - handle generic failure in text_commands - fix command /leave and a logging message - add commands /kick /ban /affiliate
author souliane <souliane@mailoo.org>
date Mon, 23 Jun 2014 15:42:56 +0200
parents 1513511a0586
children e8731b02f5ea
comparison
equal deleted inserted replaced
1081:5d89fecdf667 1082:246712d2e7bc
43 "main": "XEP_0045", 43 "main": "XEP_0045",
44 "handler": "yes", 44 "handler": "yes",
45 "description": _("""Implementation of Multi-User Chat""") 45 "description": _("""Implementation of Multi-User Chat""")
46 } 46 }
47 47
48 AFFILIATIONS = ('owner', 'admin', 'member', 'none', 'outcast')
49
48 50
49 class UnknownRoom(Exception): 51 class UnknownRoom(Exception):
50 pass 52 pass
51 53
54
52 class NotReadyYet(Exception): 55 class NotReadyYet(Exception):
53 pass 56 pass
57
54 58
55 class XEP_0045(object): 59 class XEP_0045(object):
56 # TODO: this plugin is messy, need a big cleanup/refactoring 60 # TODO: this plugin is messy, need a big cleanup/refactoring
57 61
58 def __init__(self, host): 62 def __init__(self, host):
123 if hasattr(failure.value, "condition") and failure.value.condition == 'conflict': 127 if hasattr(failure.value, "condition") and failure.value.condition == 'conflict':
124 # we have a nickname conflict, we try again with "_" suffixed to current nickname 128 # we have a nickname conflict, we try again with "_" suffixed to current nickname
125 nick += '_' 129 nick += '_'
126 return self.clients[profile].join(room_jid, nick, history_options, password).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile': profile}, errbackArgs=[room_jid, nick, history_options, password, profile]) 130 return self.clients[profile].join(room_jid, nick, history_options, password).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile': profile}, errbackArgs=[room_jid, nick, history_options, password, profile])
127 mess = D_("Error while joining the room %s" % room_jid.userhost()) 131 mess = D_("Error while joining the room %s" % room_jid.userhost())
132 try:
133 mess += " with condition '%s'" % failure.value.condition
134 except AttributeError:
135 pass
128 log.error(mess) 136 log.error(mess)
129 self.host.bridge.newAlert(mess, D_("Group chat error"), "ERROR", profile) 137 self.host.bridge.newAlert(mess, D_("Group chat error"), "ERROR", profile)
130 raise failure 138 raise failure
131 139
132 def getRoomsJoined(self, profile_key=C.PROF_KEY_NONE): 140 def getRoomsJoined(self, profile_key=C.PROF_KEY_NONE):
373 381
374 def getHandler(self, profile): 382 def getHandler(self, profile):
375 self.clients[profile] = SatMUCClient(self) 383 self.clients[profile] = SatMUCClient(self)
376 return self.clients[profile] 384 return self.clients[profile]
377 385
386 def kick(self, nick, room_jid, options={}, profile_key=C.PROF_KEY_NONE):
387 """
388 Kick a participant from the room
389 @param nick (str): nick of the user to kick
390 @param room_jid_s (JID): jid of the room
391 @param options (dict): attribute with extra info (reason, password) as in #XEP-0045
392 @param profile_key (str): %(doc_profile_key)s
393 """
394 profile = self.host.memory.getProfileName(profile_key)
395 if not self.__check_profile(profile):
396 raise exceptions.ProfileUnknownError("Unknown or disconnected profile")
397 if room_jid.userhost() not in self.clients[profile].joined_rooms:
398 raise UnknownRoom("This room has not been joined")
399 return self.clients[profile].kick(room_jid, nick, reason=options.get('reason', None))
400
401 def ban(self, entity_jid, room_jid, options={}, profile_key=C.PROF_KEY_NONE):
402 """
403 Ban an entity from the room
404 @param entity_jid (JID): bare jid of the entity to be banned
405 @param room_jid_s (JID): jid of the room
406 @param options: attribute with extra info (reason, password) as in #XEP-0045
407 @param profile_key (str): %(doc_profile_key)s
408 """
409 assert(not entity_jid.resource)
410 assert(not room_jid.resource)
411 profile = self.host.memory.getProfileName(profile_key)
412 if not self.__check_profile(profile):
413 raise exceptions.ProfileUnknownError("Unknown or disconnected profile")
414 if room_jid.userhost() not in self.clients[profile].joined_rooms:
415 raise UnknownRoom("This room has not been joined")
416 return self.clients[profile].ban(room_jid, entity_jid, reason=options.get('reason', None))
417
418 def affiliate(self, entity_jid, room_jid, options=None, profile_key=C.PROF_KEY_NONE):
419 """
420 Change the affiliation of an entity
421 @param entity_jid (JID): bare jid of the entity
422 @param room_jid_s (JID): jid of the room
423 @param options: attribute with extra info (reason, nick) as in #XEP-0045
424 @param profile_key (str): %(doc_profile_key)s
425 """
426 assert(not entity_jid.resource)
427 assert(not room_jid.resource)
428 assert('affiliation' in options)
429 profile = self.host.memory.getProfileName(profile_key)
430 if not self.__check_profile(profile):
431 raise exceptions.ProfileUnknownError("Unknown or disconnected profile")
432 if room_jid.userhost() not in self.clients[profile].joined_rooms:
433 raise UnknownRoom("This room has not been joined")
434 # TODO: handles reason and nick
435 return self.clients[profile].modifyAffiliationList(room_jid, [entity_jid], options['affiliation'])
436
378 # Text commands # 437 # Text commands #
379 438
380 def cmd_nick(self, mess_data, profile): 439 def cmd_nick(self, mess_data, profile):
381 """change nickname""" 440 """change nickname"""
382 log.debug("Catched nick command") 441 log.debug("Catched nick command")
426 return False 485 return False
427 486
428 def cmd_part(self, mess_data, profile): 487 def cmd_part(self, mess_data, profile):
429 """just a synonym of /leave""" 488 """just a synonym of /leave"""
430 return self.cmd_leave(mess_data, profile) 489 return self.cmd_leave(mess_data, profile)
490
491 def cmd_kick(self, mess_data, profile):
492 """kick a room member
493
494 @command (group): (nick)
495 - nick: the nick of the person to kick
496 """
497 log.debug("Catched kick command")
498
499 if mess_data['type'] != "groupchat":
500 self.host.plugins[C.TEXT_CMDS].feedBackWrongContext('kick', 'groupchat', mess_data, profile)
501 return False
502
503 options = mess_data["unparsed"].strip().split()
504 try:
505 nick = options[0]
506 assert(self.isNickInRoom(mess_data["to"], nick, profile))
507 except (IndexError, AssertionError):
508 feedback = _(u"You must provide a member's nick to kick.")
509 self.host.plugins[C.TEXT_CMDS].feedBack(feedback, mess_data, profile)
510 return False
511
512 d = self.kick(nick, mess_data["to"], {} if len(options) == 1 else {'reason': options[1]}, profile)
513
514 def cb(dummy):
515 mess_data['message'] = _('%s has been kicked') % nick
516 if len(options) > 1:
517 mess_data['message'] += _(' for the following reason: %s') % options[1]
518 return True
519 d.addCallback(cb)
520 return d
521
522 def cmd_ban(self, mess_data, profile):
523 """ban an entity from the room
524
525 @command (group): (JID) [reason]
526 - JID: the JID of the entity to ban
527 - reason: the reason why this entity is being banned
528 """
529 log.debug("Catched ban command")
530
531 if mess_data['type'] != "groupchat":
532 self.host.plugins[C.TEXT_CMDS].feedBackWrongContext('ban', 'groupchat', mess_data, profile)
533 return False
534
535 options = mess_data["unparsed"].strip().split()
536 try:
537 jid_s = options[0]
538 entity_jid = jid.JID(jid_s).userhostJID()
539 assert(entity_jid.user)
540 assert(entity_jid.host)
541 except (IndexError, jid.InvalidFormat, AssertionError):
542 feedback = _(u"You must provide a valid JID to ban, like in '/ban contact@example.net'")
543 self.host.plugins[C.TEXT_CMDS].feedBack(feedback, mess_data, profile)
544 return False
545
546 d = self.ban(entity_jid, mess_data["to"], {} if len(options) == 1 else {'reason': options[1]}, profile)
547
548 def cb(dummy):
549 mess_data['message'] = _('%s has been banned') % entity_jid
550 if len(options) > 1:
551 mess_data['message'] += _(' for the following reason: %s') % options[1]
552 return True
553 d.addCallback(cb)
554 return d
555
556 def cmd_affiliate(self, mess_data, profile):
557 """affiliate an entity to the room
558
559 @command (group): (JID) [owner|admin|member|none|outcast]
560 - JID: the JID of the entity to affiliate
561 - owner: grant owner privileges
562 - admin: grant admin privileges
563 - member: grant member privileges
564 - none: reset entity privileges
565 - outcast: ban entity
566 """
567 log.debug("Catched affiliate command")
568
569 if mess_data['type'] != "groupchat":
570 self.host.plugins[C.TEXT_CMDS].feedBackWrongContext('affiliate', 'groupchat', mess_data, profile)
571 return False
572
573 options = mess_data["unparsed"].strip().split()
574 try:
575 jid_s = options[0]
576 entity_jid = jid.JID(jid_s).userhostJID()
577 assert(entity_jid.user)
578 assert(entity_jid.host)
579 except (IndexError, jid.InvalidFormat, AssertionError):
580 feedback = _(u"You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'")
581 self.host.plugins[C.TEXT_CMDS].feedBack(feedback, mess_data, profile)
582 return False
583
584 affiliation = options[1] if len(options) > 1 else 'none'
585 if affiliation not in AFFILIATIONS:
586 feedback = _(u"You must provide a valid affiliation: %s") % ' '.join(AFFILIATIONS)
587 self.host.plugins[C.TEXT_CMDS].feedBack(feedback, mess_data, profile)
588 return False
589
590 d = self.affiliate(entity_jid, mess_data["to"], {'affiliation': affiliation}, profile)
591
592 def cb(dummy):
593 mess_data['message'] = _('New affiliation for %(entity)s: %(affiliation)s') % {'entity': entity_jid, 'affiliation': affiliation}
594 return True
595 d.addCallback(cb)
596 return d
431 597
432 def cmd_title(self, mess_data, profile): 598 def cmd_title(self, mess_data, profile):
433 """change room's subject""" 599 """change room's subject"""
434 log.debug("Catched title command") 600 log.debug("Catched title command")
435 601
528 if user.nick == room.nick: 694 if user.nick == room.nick:
529 # we left the room 695 # we left the room
530 room_jid_s = room.roomJID.userhost() 696 room_jid_s = room.roomJID.userhost()
531 log.info(_("Room [%(room)s] left (%(profile)s))") % {"room": room_jid_s, 697 log.info(_("Room [%(room)s] left (%(profile)s))") % {"room": room_jid_s,
532 "profile": self.parent.profile}) 698 "profile": self.parent.profile})
533 self.host.memory.delEntityCache(room.roomJID, self.parent.profile) 699 self.host.memory.delEntityCache(room.roomJID, profile_key=self.parent.profile)
534 del self.plugin_parent.clients[self.parent.profile].joined_rooms[room_jid_s] 700 del self.plugin_parent.clients[self.parent.profile].joined_rooms[room_jid_s]
535 self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile) 701 self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile)
536 else: 702 else:
537 log.debug(_("user %(nick)s left room (%(room_id)s)") % {'nick': user.nick, 'room_id': room.occupantJID.userhost()}) 703 log.debug(_("user %(nick)s left room (%(room_id)s)") % {'nick': user.nick, 'room_id': room.occupantJID.userhost()})
538 user_data = {'entity': user.entity.full() if user.entity else '', 'affiliation': user.affiliation, 'role': user.role} 704 user_data = {'entity': user.entity.full() if user.entity else '', 'affiliation': user.affiliation, 'role': user.role}