Mercurial > libervia-backend
view frontends/src/quick_frontend/quick_app.py @ 1345:b26dd78de495 frontends_multi_profiles
quick frontends: signal cache: if a signal arrives between the beginning of profile plugging, and the when the profile is actually ready, it is cached and replayed when the profile is ready.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 24 Feb 2015 20:29:57 +0100 |
parents | e31a07a5614d |
children | ba41a81d14c2 |
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.log import getLogger log = getLogger(__name__) from sat.core.i18n import _ from sat.core import exceptions from sat_frontends.tools import jid from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager from sat_frontends.quick_frontend import quick_chat from sat_frontends.quick_frontend.constants import Const as C import sys from collections import OrderedDict 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 = str class ProfileManager(object): """Class managing all data relative to one profile, and plugging in mechanism""" host = None bridge = None cache_keys_to_get = ['avatar'] def __init__(self, profile): self.profile = profile self.whoami = None self.data = {} def __getitem__(self, key): return self.data[key] def __setitem__(self, key, value): self.data[key] = value def plug(self): """Plug the profile to the host""" # we get the essential params self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile, callback=self._plug_profile_jid, errback=self._getParamError) def _plug_profile_jid(self, _jid): self.whoami = jid.JID(_jid) self.bridge.asyncGetParamA("autoconnect", "Connection", profile_key=self.profile, callback=self._plug_profile_autoconnect, errback=self._getParamError) def _plug_profile_autoconnect(self, value_str): autoconnect = C.bool(value_str) if autoconnect and not self.bridge.isConnected(self.profile): self.host.asyncConnect(self.profile, callback=lambda dummy: self._plug_profile_afterconnect()) else: self._plug_profile_afterconnect() def _plug_profile_afterconnect(self): # Profile can be connected or not # we get cached data self.host.bridge.getEntitiesData([], ProfileManager.cache_keys_to_get, profile=self.profile, callback=self._plug_profile_gotCachedValues, errback=self._plug_profile_failedCachedValues) def _plug_profile_failedCachedValues(self, failure): log.error("Couldn't get cached values: {}".format(failure)) self._plug_profile_gotCachedValues({}) def _plug_profile_gotCachedValues(self, cached_values): # TODO: watched plugin # add the contact list and its listener contact_list = self.host.addContactList(self.profile) self.host.contact_lists[self.profile] = contact_list for entity, data in cached_values.iteritems(): for key, value in data.iteritems(): contact_list.setCache(jid.JID(entity), key, value) if not self.bridge.isConnected(self.profile): self.host.setStatusOnline(False, profile=self.profile) else: self.host.setStatusOnline(True, profile=self.profile) contact_list.fill() #The waiting subscription requests self.bridge.getWaitingSub(self.profile, callback=self._plug_profile_gotWaitingSub) def _plug_profile_gotWaitingSub(self, waiting_sub): for sub in waiting_sub: self.host.subscribeHandler(waiting_sub[sub], sub, self.profile) self.bridge.getRoomsJoined(self.profile, callback=self._plug_profile_gotRoomsJoined) def _plug_profile_gotRoomsJoined(self, rooms_args): #Now we open the MUC window where we already are: for room_args in rooms_args: self.host.roomJoinedHandler(*room_args, profile=self.profile) self.bridge.getRoomsSubjects(self.profile, callback=self._plug_profile_gotRoomsSubjects) def _plug_profile_gotRoomsSubjects(self, subjects_args): for subject_args in subjects_args: self.host.roomNewSubjectHandler(*subject_args, profile=self.profile) #Presence must be requested after rooms are filled self.host.bridge.getPresenceStatuses(self.profile, callback=self._plug_profile_gotPresences) def _plug_profile_gotPresences(self, presences): def gotEntityData(data, contact): for key in ('avatar', 'nick'): if key in data: self.host.entityDataUpdatedHandler(contact, key, data[key], self.profile) for contact in presences: for res in presences[contact]: jabber_id = ('%s/%s' % (jid.JID(contact).bare, res)) if res else contact show = presences[contact][res][0] priority = presences[contact][res][1] statuses = presences[contact][res][2] self.host.presenceUpdateHandler(jabber_id, show, priority, statuses, self.profile) self.host.bridge.getEntityData(contact, ['avatar', 'nick'], self.profile, callback=lambda data, contact=contact: gotEntityData(data, contact), errback=lambda failure, contact=contact: log.debug("No cache data for {}".format(contact))) #Finaly, we get the waiting confirmation requests self.bridge.getWaitingConf(self.profile, callback=self._plug_profile_gotWaitingConf) def _plug_profile_gotWaitingConf(self, waiting_confs): for confirm_id, confirm_type, data in waiting_confs: self.host.askConfirmationHandler(confirm_id, confirm_type, data, self.profile) # At this point, profile should be fully plugged # and we launch frontend specific method self.host.profilePlugged(self.profile) def _getParamError(self, ignore): log.error(_("Can't get profile parameter")) class ProfilesManager(object): """Class managing collection of profiles""" def __init__(self): self._profiles = {} def __contains__(self, profile): return profile in self._profiles def __iter__(self): return self._profiles.iterkeys() def __getitem__(self, profile): return self._profiles[profile] def __len__(self): return len(self._profiles) def plug(self, profile): if profile in self._profiles: raise exceptions.ConflictError('A profile of the name [{}] is already plugged'.format(profile)) self._profiles[profile] = ProfileManager(profile) self._profiles[profile].plug() def unplug(self, profile): if profile not in self._profiles: raise ValueError('The profile [{}] is not plugged'.format(profile)) # remove the contact list and its listener host = self._profiles[profile].host host.contact_lists[profile].onDelete() del host.contact_lists[profile] del self._profiles[profile] def chooseOneProfile(self): return self._profiles.keys()[0] class QuickApp(object): """This class contain the main methods needed for the frontend""" def __init__(self, create_bridge, check_options=None): """Create a frontend application @param create_bridge: method to use to create the Bridge @param check_options: method to call to check options (usually command line arguments) """ ProfileManager.host = self self.profiles = ProfilesManager() self.ready_profiles = set() # profiles which are connected and ready self.signals_cache = {} # used to keep signal received between start of plug_profile and when the profile is actualy ready self.contact_lists = {} self.widgets = QuickWidgetsManager(self) if check_options is not None: self.options = check_options() else: self.options = None # widgets self.selected_widget = None # widget currently selected (must be filled by frontend) # listeners self._listeners = {} # key: listener type ("avatar", "selected", etc), value: list of callbacks ## bridge ## try: self.bridge = create_bridge() except exceptions.BridgeExceptionNoService: print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) sys.exit(1) except exceptions.BridgeInitError: print(_(u"Can't init bridge")) sys.exit(1) ProfileManager.bridge = self.bridge self.registerSignal("connected") self.registerSignal("disconnected") self.registerSignal("newContact") self.registerSignal("newMessage") self.registerSignal("newAlert") self.registerSignal("presenceUpdate") self.registerSignal("subscribe") self.registerSignal("paramUpdate") self.registerSignal("contactDeleted") self.registerSignal("entityDataUpdated") self.registerSignal("askConfirmation") self.registerSignal("actionResult") self.registerSignal("actionResultExt", self.actionResultHandler) self.registerSignal("roomJoined", iface="plugin") self.registerSignal("roomLeft", iface="plugin") self.registerSignal("roomUserJoined", iface="plugin") self.registerSignal("roomUserLeft", iface="plugin") self.registerSignal("roomUserChangedNick", iface="plugin") self.registerSignal("roomNewSubject", iface="plugin") self.registerSignal("tarotGameStarted", iface="plugin") self.registerSignal("tarotGameNew", iface="plugin") self.registerSignal("tarotGameChooseContrat", iface="plugin") self.registerSignal("tarotGameShowCards", iface="plugin") self.registerSignal("tarotGameYourTurn", iface="plugin") self.registerSignal("tarotGameScore", iface="plugin") self.registerSignal("tarotGameCardsPlayed", iface="plugin") self.registerSignal("tarotGameInvalidCards", iface="plugin") self.registerSignal("quizGameStarted", iface="plugin") self.registerSignal("quizGameNew", iface="plugin") self.registerSignal("quizGameQuestion", iface="plugin") self.registerSignal("quizGamePlayerBuzzed", iface="plugin") self.registerSignal("quizGamePlayerSays", iface="plugin") self.registerSignal("quizGameAnswerResult", iface="plugin") self.registerSignal("quizGameTimerExpired", iface="plugin") self.registerSignal("quizGameTimerRestarted", iface="plugin") self.registerSignal("chatStateReceived", iface="plugin") self.current_action_ids = set() # FIXME: to be removed self.current_action_ids_cb = {} # FIXME: to be removed self.media_dir = self.bridge.getConfig('', 'media_dir') @property def current_profile(self): """Profile that a user would expect to use""" try: return self.selected_widget.profile except (TypeError, AttributeError): return self.profiles.chooseOneProfile() @property def visible_widgets(self): """widgets currently visible (must be implemented by frontend)""" raise NotImplementedError def registerSignal(self, function_name, handler=None, iface="core", with_profile=True): """Register a handler for a signal @param function_name (str): name of the signal to handle @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (function_name + 'Handler') @param iface (str): interface of the bridge to use ('core' or 'plugin') @param with_profile (boolean): True if the signal concerns a specific profile, in that case the profile name has to be passed by the caller """ if handler is None: handler = getattr(self, "{}{}".format(function_name, 'Handler')) if not with_profile: self.bridge.register(function_name, handler, iface) return def signalReceived(*args, **kwargs): profile = kwargs.get('profile') if profile is None: if not args: raise exceptions.ProfileNotSetError profile = args[-1] if profile is not None: if not self.check_profile(profile): if profile in self.profiles: # profile is not ready but is in self.profiles, that's mean that it's being connecting and we need to cache the signal self.signals_cache.setdefault(profile, []).append((function_name, handler, args, kwargs)) return # we ignore signal for profiles we don't manage handler(*args, **kwargs) self.bridge.register(function_name, signalReceived, iface) def addListener(self, type_, callback, profiles_filter=None): """Add a listener for an event /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget) @param type_: type of event, can be: - avatar: called when avatar data is updated args: (entity, avatar file, profile) - nick: called when nick data is updated args: (entity, new_nick, profile) - presence: called when a presence is received args: (entity, show, priority, statuses, profile) @param callback: method to call on event @param profiles_filter (set[unicode]): if set and not empty, the listener will be callable only by one of the given profiles. """ assert type_ in C.LISTENERS self._listeners.setdefault(type_, OrderedDict())[callback] = profiles_filter def removeListener(self, type_, callback): """Remove a callback from listeners @param type_: same as for [addListener] @param callback: callback to remove """ assert type_ in C.LISTENERS self._listeners[type_].pop(callback) def callListeners(self, type_, *args, **kwargs): """Call the methods which listen type_ event. If a profiles filter has been register with a listener and profile argument is not None, the listener will be called only if profile is in the profiles filter list. @param type_: same as for [addListener] @param *args: arguments sent to callback @param **kwargs: keywords argument, mainly used to pass "profile" when needed """ assert type_ in C.LISTENERS try: listeners = self._listeners[type_] except KeyError: pass else: profile = kwargs.get("profile") for listener, profiles_filter in listeners.iteritems(): if profile is None or not profiles_filter or profile in profiles_filter: listener(*args, **kwargs) def check_profile(self, profile): """Tell if the profile is currently followed by the application, and ready""" return profile in self.ready_profiles def postInit(self, profile_manager): """Must be called after initialization is done, do all automatic task (auto plug profile) @param profile_manager: instance of a subclass of Quick_frontend.QuickProfileManager """ if self.options and self.options.profile: profile_manager.autoconnect([self.options.profile]) def profilePlugged(self, profile): """Method called when the profile is fully plugged, to launch frontend specific workflow /!\ if you override the method and don't call the parent, be sure to add the profile to ready_profiles ! if you don't, all signals will stay in cache @param profile(unicode): %(doc_profile)s """ self.ready_profiles.add(profile) # profile is ready, we can call send signals that where is cache cached_signals = self.signals_cache.pop(profile, []) for function_name, handler, args, kwargs in cached_signals: log.debug(u"Calling cached signal [%s] with args %s and kwargs %s" % (function_name, args, kwargs)) def asyncConnect(self, profile, callback=None, errback=None): if not callback: callback = lambda dummy: None if not errback: def errback(failure): log.error(_(u"Can't connect profile [%s]") % failure) if failure.module.startswith('twisted.words.protocols.jabber') and failure.condition == "not-authorized": self.launchAction(C.CHANGE_XMPP_PASSWD_ID, {}, profile=profile) else: self.showDialog(failure.message, failure.fullname, 'error') self.bridge.asyncConnect(profile, callback=callback, errback=errback) def plug_profiles(self, profiles): """Tell application which profiles must be used @param profiles: list of valid profile names """ self.plugging_profiles() for profile in profiles: self.profiles.plug(profile) def plugging_profiles(self): """Method to subclass to manage frontend specific things to do will be called when profiles are choosen and are to be plugged soon """ pass def unplug_profile(self, profile): """Tell the application to not follow anymore the profile""" if not profile in self.profiles: raise ValueError("The profile [{}] is not plugged".format(profile)) self.profiles.unplug(profile) def clear_profile(self): self.profiles.clear() def addContactList(self, profile): """Method to subclass to add a contact list widget will be called on each profile session build @return: a ContactList widget """ return NotImplementedError def newWidget(self, widget): raise NotImplementedError def connectedHandler(self, profile): """called when the connection is made""" log.debug(_("Connected")) self.setStatusOnline(True, profile=profile) def disconnectedHandler(self, profile): """called when the connection is closed""" log.debug(_("Disconnected")) self.contact_lists[profile].clearContacts() self.setStatusOnline(False, profile=profile) def newContactHandler(self, JabberId, attributes, groups, profile): entity = jid.JID(JabberId) _groups = list(groups) self.contact_lists[profile].setContact(entity, _groups, attributes, in_roster=True) def newMessageHandler(self, from_jid_s, msg, type_, to_jid_s, extra, profile): from_jid = jid.JID(from_jid_s) to_jid = jid.JID(to_jid_s) from_me = from_jid.bare == self.profiles[profile].whoami.bare target = to_jid if from_me else from_jid chat_type = C.CHAT_GROUP if type_ == C.MESS_TYPE_GROUPCHAT else C.CHAT_ONE2ONE contact_list = self.contact_lists[profile] chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, target, type_=chat_type, on_new_widget=None, profile=profile) self.current_action_ids = set() # FIXME: to be removed self.current_action_ids_cb = {} # FIXME: to be removed if not from_jid in contact_list and from_jid.bare != self.profiles[profile].whoami.bare: #XXX: needed to show entities which haven't sent any # presence information and which are not in roster contact_list.setContact(from_jid) # we display the message in the widget chat_widget.newMessage(from_jid, target, msg, type_, extra, profile) # ContactList alert visible = False for widget in self.visible_widgets: if isinstance(widget, quick_chat.QuickChat) and widget.manageMessage(from_jid, type_): visible = True break if not visible: contact_list.setAlert(from_jid.bare if type_ == C.MESS_TYPE_GROUPCHAT else from_jid) def sendMessage(self, to_jid, message, subject='', mess_type="auto", extra={}, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): if callback is None: callback = lambda dummy=None: None # FIXME: optional argument is here because pyjamas doesn't support callback without arg with json proxy if errback is None: errback = lambda failure: self.showDialog(failure.fullname, failure.message, "error") self.bridge.sendMessage(to_jid, message, subject, mess_type, extra, profile_key, callback=callback, errback=errback) def newAlertHandler(self, msg, title, alert_type, profile): assert alert_type in ['INFO', 'ERROR'] self.showDialog(unicode(msg), unicode(title), alert_type.lower()) def setStatusOnline(self, online=True, show="", statuses={}, profile=C.PROF_KEY_NONE): raise NotImplementedError def presenceUpdateHandler(self, entity_s, show, priority, statuses, profile): log.debug(_("presence update for %(entity)s (show=%(show)s, priority=%(priority)s, statuses=%(statuses)s) [profile:%(profile)s]") % {'entity': entity_s, C.PRESENCE_SHOW: show, C.PRESENCE_PRIORITY: priority, C.PRESENCE_STATUSES: statuses, 'profile': profile}) entity = jid.JID(entity_s) if entity == self.profiles[profile].whoami: if show == "unavailable": self.setStatusOnline(False, profile=profile) else: self.setStatusOnline(True, show, statuses, profile=profile) return # #FIXME: must be moved in a plugin # if entity.bare in self.profiles[profile].data.get('watched',[]) and not entity.bare in self.profiles[profile]['onlineContact']: # self.showAlert(_("Watched jid [%s] is connected !") % entity.bare) self.callListeners('presence', entity, show, priority, statuses, profile=profile) def roomJoinedHandler(self, room_jid_s, room_nicks, user_nick, profile): """Called when a MUC room is joined""" log.debug("Room [%(room_jid)s] joined by %(profile)s, users presents:%(users)s" % {'room_jid': room_jid_s, 'profile': profile, 'users': room_nicks}) room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_='group', profile=profile) chat_widget.setUserNick(user_nick) chat_widget.id = room_jid # FIXME: to be removed chat_widget.setPresents(list(set([user_nick] + room_nicks))) self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP) def roomLeftHandler(self, room_jid_s, profile): """Called when a MUC room is left""" log.debug("Room [%(room_jid)s] left by %(profile)s" % {'room_jid': room_jid_s, 'profile': profile}) del self.chat_wins[room_jid_s] self.contact_lists[profile].remove(jid.JID(room_jid_s)) def roomUserJoinedHandler(self, room_jid_s, user_nick, user_data, profile): """Called when an user joined a MUC room""" room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_='group', profile=profile) chat_widget.replaceUser(user_nick) log.debug("user [%(user_nick)s] joined room [%(room_jid)s]" % {'user_nick': user_nick, 'room_jid': room_jid}) def roomUserLeftHandler(self, room_jid_s, user_nick, user_data, profile): """Called when an user joined a MUC room""" room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_='group', profile=profile) chat_widget.removeUser(user_nick) log.debug("user [%(user_nick)s] left room [%(room_jid)s]" % {'user_nick': user_nick, 'room_jid': room_jid}) def roomUserChangedNickHandler(self, room_jid_s, old_nick, new_nick, profile): """Called when an user joined a MUC room""" room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_='group', profile=profile) chat_widget.changeUserNick(old_nick, new_nick) log.debug("user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]" % {'old_nick': old_nick, 'new_nick': new_nick, 'room_jid': room_jid}) def roomNewSubjectHandler(self, room_jid_s, subject, profile): """Called when subject of MUC room change""" room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_='group', profile=profile) chat_widget.setSubject(subject) log.debug("new subject for room [%(room_jid)s]: %(subject)s" % {'room_jid': room_jid, "subject": subject}) def tarotGameStartedHandler(self, room_jid_s, referee, players, profile): log.debug(_("Tarot Game Started \o/")) room_jid = jid.JID(room_jid_s) chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_='group', profile=profile) chat_widget.startGame("Tarot", referee, players) log.debug("new Tarot game started by [%(referee)s] in room [%(room_jid)s] with %(players)s" % {'referee': referee, 'room_jid': room_jid, 'players': [str(player) for player in players]}) def tarotGameNewHandler(self, room_jid, hand, profile): log.debug(_("New Tarot Game")) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").newGame(hand) def tarotGameChooseContratHandler(self, room_jid, xml_data, profile): """Called when the player has to select his contrat""" log.debug(_("Tarot: need to select a contrat")) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").chooseContrat(xml_data) def tarotGameShowCardsHandler(self, room_jid, game_stage, cards, data, profile): log.debug(_("Show cards")) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").showCards(game_stage, cards, data) def tarotGameYourTurnHandler(self, room_jid, profile): log.debug(_("My turn to play")) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").myTurn() def tarotGameScoreHandler(self, room_jid, xml_data, winners, loosers, profile): """Called when the game is finished and the score are updated""" log.debug(_("Tarot: score received")) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").showScores(xml_data, winners, loosers) def tarotGameCardsPlayedHandler(self, room_jid, player, cards, profile): log.debug(_("Card(s) played (%(player)s): %(cards)s") % {"player": player, "cards": cards}) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").cardsPlayed(player, cards) def tarotGameInvalidCardsHandler(self, room_jid, phase, played_cards, invalid_cards, profile): log.debug(_("Cards played are not valid: %s") % invalid_cards) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Tarot").invalidCards(phase, played_cards, invalid_cards) def quizGameStartedHandler(self, room_jid, referee, players, profile): log.debug(_("Quiz Game Started \o/")) if room_jid in self.chat_wins: self.chat_wins[room_jid].startGame("Quiz", referee, players) log.debug(_("new Quiz game started by [%(referee)s] in room [%(room_jid)s] with %(players)s") % {'referee': referee, 'room_jid': room_jid, 'players': [str(player) for player in players]}) def quizGameNewHandler(self, room_jid, data, profile): log.debug(_("New Quiz Game")) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGameNewHandler(data) def quizGameQuestionHandler(self, room_jid, question_id, question, timer, profile): """Called when a new question is asked""" log.debug(_(u"Quiz: new question: %s") % question) if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGameQuestionHandler(question_id, question, timer) def quizGamePlayerBuzzedHandler(self, room_jid, player, pause, profile): """Called when a player pushed the buzzer""" if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGamePlayerBuzzedHandler(player, pause) def quizGamePlayerSaysHandler(self, room_jid, player, text, delay, profile): """Called when a player say something""" if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGamePlayerSaysHandler(player, text, delay) def quizGameAnswerResultHandler(self, room_jid, player, good_answer, score, profile): """Called when a player say something""" if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGameAnswerResultHandler(player, good_answer, score) def quizGameTimerExpiredHandler(self, room_jid, profile): """Called when nobody answered the question in time""" if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGameTimerExpiredHandler() def quizGameTimerRestartedHandler(self, room_jid, time_left, profile): """Called when the question is not answered, and we still have time""" if room_jid in self.chat_wins: self.chat_wins[room_jid].getGame("Quiz").quizGameTimerRestartedHandler(time_left) def chatStateReceivedHandler(self, from_jid_s, state, profile): """Called when a new chat state is received. @param from_jid_s: JID of the contact who sent his state, or '@ALL@' @param state: new state (string) @profile: current profile """ from_jid = jid.JID(from_jid_s) if from_jid_s != C.ENTITY_ALL else C.ENTITY_ALL for widget in self.widgets.getWidgets(quick_chat.QuickChat): if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare: widget.updateChatState(from_jid, state) def _subscribe_cb(self, answer, data): entity, profile = data type_ = "subscribed" if answer else "unsubscribed" self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile) def subscribeHandler(self, type, raw_jid, profile): """Called when a subsciption management signal is received""" entity = jid.JID(raw_jid) if type == "subscribed": # this is a subscription confirmation, we just have to inform user self.showDialog(_("The contact %s has accepted your subscription") % entity.bare, _('Subscription confirmation')) elif type == "unsubscribed": # this is a subscription refusal, we just have to inform user self.showDialog(_("The contact %s has refused your subscription") % entity.bare, _('Subscription refusal'), 'error') elif type == "subscribe": # this is a subscriptionn request, we have to ask for user confirmation self.showDialog(_("The contact %s wants to subscribe to your presence.\nDo you accept ?") % entity.bare, _('Subscription confirmation'), 'yes/no', answer_cb=self._subscribe_cb, answer_data=(entity, profile)) def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): raise NotImplementedError def showAlert(self, message): pass #FIXME def paramUpdateHandler(self, name, value, namespace, profile): log.debug(_("param update: [%(namespace)s] %(name)s = %(value)s") % {'namespace': namespace, 'name': name, 'value': value}) if (namespace, name) == ("Connection", "JabberID"): log.debug(_("Changing JID to %s") % value) self.profiles[profile].whoami = jid.JID(value) elif (namespace, name) == ("Misc", "Watched"): self.profiles[profile]['watched'] = value.split() elif (namespace, name) == ('General', C.SHOW_OFFLINE_CONTACTS): self.contact_lists[profile].showOfflineContacts(C.bool(value)) elif (namespace, name) == ('General', C.SHOW_EMPTY_GROUPS): self.contact_lists[profile].showEmptyGroups(C.bool(value)) def contactDeletedHandler(self, jid_s, profile): target = jid.JID(jid_s) self.contact_lists[profile].remove(target) def entityDataUpdatedHandler(self, entity_s, key, value, profile): entity = jid.JID(entity_s) if key == "nick": if entity in self.contact_lists[profile]: self.contact_lists[profile].setCache(entity, 'nick', value) self.callListeners('nick', entity, value, profile=profile) elif key == "avatar": if entity in self.contact_lists[profile]: def gotFilename(filename): self.contact_lists[profile].setCache(entity, 'avatar', filename) self.callListeners('avatar', entity, filename, profile=profile) self.bridge.getAvatarFile(value, callback=gotFilename) def askConfirmationHandler(self, confirm_id, confirm_type, data, profile): raise NotImplementedError def actionResultHandler(self, type, id, data, profile): raise NotImplementedError def launchAction(self, callback_id, data=None, callback=None, profile="@NONE@"): """Launch a dynamic action @param callback_id: id of the action to launch @param data: data needed only for certain actions @param callback: if not None and 'validated' key is present, it will be called with the following parameters: - callback_id - data - profile_key @param profile_key: %(doc_profile)s """ raise NotImplementedError def onExit(self): """Must be called when the frontend is terminating""" to_unplug = [] for profile in self.profiles: if self.bridge.isConnected(profile): if C.bool(self.bridge.getParamA("autodisconnect", "Connection", profile_key=profile)): #The user wants autodisconnection self.bridge.disconnect(profile) to_unplug.append(profile) for profile in to_unplug: self.unplug_profile(profile)