Mercurial > libervia-backend
view frontends/src/quick_frontend/quick_chat.py @ 1304:1a61b18703c4 frontends_multi_profiles
quick frontend (quick widgets): class' __name__ method is used for classes_map hash because the use of class directly was causing bugs with pyjamas (difficult to find, several MicroblogPanel instances were added only once in Libervia's TabPanel, hash method seemed buggy)
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 06 Feb 2015 19:05:51 +0100 |
parents | faa1129559b8 |
children | ebf72fe68d1c |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # helper class for making a SAT frontend # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from sat.core.i18n import _ from sat.core.log import getLogger log = getLogger(__name__) from sat_frontends.tools import jid from sat_frontends.quick_frontend import quick_widgets from sat_frontends.quick_frontend.constants import Const as C try: # FIXME: to be removed when an acceptable solution is here unicode('') # XXX: unicode doesn't exist in pyjamas except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options unicode = lambda x: str(x) class QuickChat(quick_widgets.QuickWidget): def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None): """ @param type_: can be C.CHAT_ONE2ONE for single conversation or C.CHAT_GROUP for chat à la IRC """ quick_widgets.QuickWidget.__init__(self, host, target, profiles=profiles) assert type_ in (C.CHAT_ONE2ONE, C.CHAT_GROUP) if type_ == C.CHAT_GROUP and target.resource: raise ValueError("A group chat entity can't have a resource") self.current_target = target self.type = type_ self.id = "" # FIXME: to be removed self.nick = None self.occupants = set() def __str__(self): return u"Chat Widget [target: {}, type: {}, profile: {}]".format(self.target, self.type, self.profile) @staticmethod def getWidgetHash(target, profile): return (unicode(profile), target.bare) @staticmethod def getPrivateHash(target, profile): """Get unique hash for private conversations This method should be used with force_hash to get unique widget for private MUC conversations """ return (unicode(profile), target) def addTarget(self, target): super(QuickChat, self).addTarget(target) if target.resource: self.current_target = target # FIXME: tmp, must use resource priority throught contactList instead @property def target(self): if self.type == C.CHAT_GROUP: return self.current_target.bare return self.current_target def manageMessage(self, entity, mess_type): """Tell if this chat widget manage an entity and message type couple @param entity (jid.JID): (full) jid of the sending entity @param mess_type (str): message type as given by newMessage @return (bool): True if this Chat Widget manage this couple """ if self.type == C.CHAT_GROUP: if mess_type == C.MESS_TYPE_GROUPCHAT and self.target == entity.bare: return True else: if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets: return True return False def setPresents(self, nicks): """Set the users presents in the contact list for a group chat @param nicks: list of nicknames """ log.debug (_("Adding users %s to room") % nicks) if self.type != C.CHAT_GROUP: log.error (_("[INTERNAL] trying to set presents nicks for a non group chat window")) raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here self.occupants.update(nicks) def replaceUser(self, nick, show_info=True): """Add user if it is not in the group list""" log.debug (_("Replacing user %s") % nick) if self.type != C.CHAT_GROUP: log.error (_("[INTERNAL] trying to replace user for a non group chat window")) raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here len_before = len(self.occupants) self.occupants.add(nick) if len_before != len(self.occupants) and show_info: self.printInfo("=> %s has joined the room" % nick) def removeUser(self, nick, show_info=True): """Remove a user from the group list""" log.debug(_("Removing user %s") % nick) if self.type != C.CHAT_GROUP: log.error (_("[INTERNAL] trying to remove user for a non group chat window")) raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here self.occupants.remove(nick) if show_info: self.printInfo("<= %s has left the room" % nick) def setUserNick(self, nick): """Set the nick of the user, usefull for e.g. change the color of the user""" self.nick = nick def getUserNick(self): return unicode(self.nick) def changeUserNick(self, old_nick, new_nick): """Change nick of a user in group list""" log.debug(_("Changing nick of user %(old_nick)s to %(new_nick)s") % {"old_nick": old_nick, "new_nick": new_nick}) if self.type != C.CHAT_GROUP: log.error (_("[INTERNAL] trying to change user nick for a non group chat window")) raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here self.removeUser(old_nick, show_info=False) self.replaceUser(new_nick, show_info=False) self.printInfo("%s is now known as %s" % (old_nick, new_nick)) def setSubject(self, subject): """Set title for a group chat""" log.debug(_("Setting subject to %s") % subject) if self.type != C.CHAT_GROUP: log.error (_("[INTERNAL] trying to set subject for a non group chat window")) raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here def afterHistoryPrint(self): """Refresh or scroll down the focus after the history is printed""" pass def historyPrint(self, size=C.HISTORY_LIMIT_DEFAULT, search='', profile='@NONE@'): """Print the current history @param size (int): number of messages @param search (str): pattern to filter the history results @param profile (str): %(doc_profile)s """ log.debug(_("now we print the history (%d messages)") % size) def onHistory(history): for line in history: timestamp, from_jid, to_jid, message, type_, extra = line # FIXME: extra is unused ! if ((self.type == C.CHAT_GROUP and type_ != C.MESS_TYPE_GROUPCHAT) or (self.type == C.CHAT_ONE2ONE and type_ == C.MESS_TYPE_GROUPCHAT)): continue self.printMessage(jid.JID(from_jid), message, {'timestamp':timestamp}, profile) self.afterHistoryPrint() def onHistoryError(err): log.error(_("Can't get history")) target = self.target.bare self.host.bridge.getHistory(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, search, profile, callback=onHistory, errback=onHistoryError) def _get_nick(self, entity): """Return nick of this entity when possible""" if self.type == C.CHAT_GROUP: return entity.resource contact_list = self.host.contact_lists[self.profile] if entity.bare in contact_list: return contact_list.getCache(entity,'nick') or contact_list.getCache(entity,'name') or entity.node or entity return entity.node or entity def onPrivateCreated(self, widget): """Method called when a new widget for private conversation (MUC) is created""" raise NotImplementedError def getOrCreatePrivateWidget(self, entity): """Create a widget for private conversation, or get it if it already exists @param entity: full jid of the target """ return self.host.widgets.getOrCreateWidget(QuickChat, entity, type_=C.CHAT_ONE2ONE, force_hash=self.getPrivateHash(self.profile, entity), on_new_widget=self.onPrivateCreated, profile=self.profile) # we force hash to have a new widget, not this one again def newMessage(self, from_jid, target, msg, type_, extra, profile): if self.type == C.CHAT_GROUP and target.resource and type_ != C.MESS_TYPE_GROUPCHAT: # we have a private message, we forward it to a private conversation widget chat_widget = self.getOrCreatePrivateWidget(target) chat_widget.newMessage(from_jid, target, msg, type_, extra, profile) else: if type_ == C.MESS_TYPE_INFO: self.printInfo(msg, extra=extra) else: self.printMessage(from_jid, msg, extra, profile) def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE): """Print message in chat window. Must be implemented by child class""" nick = self._get_nick(from_jid) mymess = (from_jid.resource == self.nick) if self.type == C.CHAT_GROUP else (from_jid.bare == self.host.profiles[profile].whoami.bare) #mymess = True if message comes from local user if msg.startswith('/me '): self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', extra=extra) return return nick, mymess def printInfo(self, msg, type_='normal', extra=None): """Print general info @param msg: message to print @type_: one of: normal: general info like "toto has joined the room" me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist" @param extra (dict): message data """ raise NotImplementedError def startGame(self, game_type, referee, players): """Configure the chat window to start a game""" #No need to raise an error as game are not mandatory log.warning(_('startGame is not implemented in this frontend')) def getGame(self, game_type): """Return class managing the game type""" #No need to raise an error as game are not mandatory log.warning(_('getGame is not implemented in this frontend')) def updateChatState(self, from_jid, state): """Set the chat state (XEP-0085) of the contact. @param state: the new chat state """ raise NotImplementedError quick_widgets.register(QuickChat)