Mercurial > libervia-backend
view frontends/src/quick_frontend/quick_app.py @ 347:ea3e1b82dd79
core: contact deletion from roster if we have no subscription to it (behaviour may change in futur)
quick frontend: groups is updated in contactList in case of roster push
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 29 May 2011 16:12:08 +0200 |
parents | f19771d2e63b |
children | 141eeb7cd9e6 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ helper class for making a SAT frontend Copyright (C) 2009, 2010, 2011 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 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ from logging import debug, info, error from sat.tools.jid import JID from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService from optparse import OptionParser import pdb import gettext gettext.install('sat_frontend', "../i18n", unicode=True) class QuickApp(): """This class contain the main methods needed for the frontend""" def __init__(self, single_profile=True): self.rosterList = {} self.profiles = {} self.single_profile = single_profile self.check_options() ## bridge ## try: self.bridge=DBusBridgeFrontend() except BridgeExceptionNoService: print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) import sys sys.exit(1) self.bridge.register("connected", self.connected) self.bridge.register("disconnected", self.disconnected) self.bridge.register("connectionError", self.connectionError) self.bridge.register("newContact", self.newContact) self.bridge.register("newMessage", self.newMessage) self.bridge.register("newAlert", self.newAlert) self.bridge.register("presenceUpdate", self.presenceUpdate) self.bridge.register("roomJoined", self.roomJoined) self.bridge.register("roomUserJoined", self.roomUserJoined) self.bridge.register("roomUserLeft", self.roomUserLeft) self.bridge.register("roomNewSubject", self.roomNewSubject) self.bridge.register("tarotGameStarted", self.tarotGameStarted) self.bridge.register("tarotGameNew", self.tarotGameNew) self.bridge.register("tarotGameChooseContrat", self.tarotChooseContrat) self.bridge.register("tarotGameShowCards", self.tarotShowCards) self.bridge.register("tarotGameYourTurn", self.tarotMyTurn) self.bridge.register("tarotGameScore", self.tarotScore) self.bridge.register("tarotGameCardsPlayed", self.tarotCardsPlayed) self.bridge.register("tarotGameInvalidCards", self.tarotInvalidCards) self.bridge.register("subscribe", self.subscribe) self.bridge.register("paramUpdate", self.paramUpdate) self.bridge.register("contactDeleted", self.contactDeleted) self.bridge.register("updatedValue", self.updatedValue, "request") self.bridge.register("askConfirmation", self.askConfirmation, "request") self.bridge.register("actionResult", self.actionResult, "request") self.bridge.register("actionResultExt", self.actionResult, "request") self.current_action_ids = set() self.current_action_ids_cb = {} def check_profile(self, profile): """Tell if the profile is currently followed by the application""" return profile in self.profiles.keys() def postInit(self): """Must be called after initialization is done, do all automatic task (auto plug profile)""" if self.options.profile: if not self.bridge.getProfileName(self.options.profile): error(_("Trying to plug an unknown profile (%s)" % self.options.profile)) else: self.plug_profile(self.options.profile) def check_options(self): """Check command line options""" usage=_(""" %prog [options] %prog --help for options list """) parser = OptionParser(usage=usage) parser.add_option("-p", "--profile", help=_("Select the profile to use")) (self.options, args) = parser.parse_args() if self.options.profile: self.options.profile = self.options.profile.decode('utf-8') return args def plug_profile(self, profile_key='@DEFAULT@'): """Tell application which profile must be used""" if self.single_profile and self.profiles: error(_('There is already one profile plugged (we are in single profile mode) !')) return profile = self.bridge.getProfileName(profile_key) if not profile: error(_("The profile asked doesn't exist")) return if self.profiles.has_key(profile): warning(_("The profile is already plugged")) return self.profiles[profile]={} if self.single_profile: self.profile = profile ###now we get the essential params### self.profiles[profile]['whoami']=JID(self.bridge.getParamA("JabberID","Connection", profile_key=profile)) autoconnect = self.bridge.getParamA("autoconnect","Connection", profile_key=profile) == "true" self.profiles[profile]['watched']=self.bridge.getParamA("Watched", "Misc", profile_key=profile).split() #TODO: put this in a plugin ## misc ## self.profiles[profile]['onlineContact'] = set() #FIXME: temporary #TODO: gof: manage multi-profiles here if not self.bridge.isConnected(profile): self.setStatusOnline(False) else: self.setStatusOnline(True) ### now we fill the contact list ### for contact in self.bridge.getContacts(profile): self.newContact(contact[0], contact[1], contact[2], profile) presences = self.bridge.getPresenceStatus(profile) for contact in presences: for res in presences[contact]: jabber_id = contact+('/'+res if res else '') show = presences[contact][res][0] priority = presences[contact][res][1] statuses = presences[contact][res][2] self.presenceUpdate(jabber_id, show, priority, statuses, profile) #The waiting subscription requests waitingSub = self.bridge.getWaitingSub(profile) for sub in waitingSub: self.subscribe(waitingSub[sub], sub, profile) #Now we open the MUC window where we already are: for room_args in self.bridge.getRoomJoined(profile): self.roomJoined(*room_args, profile=profile) for subject_args in self.bridge.getRoomSubjects(profile): self.roomNewSubject(*subject_args, profile=profile) if autoconnect and not self.bridge.isConnected(profile_key): #Does the user want autoconnection ? self.bridge.connect(profile_key) def unplug_profile(self, profile): """Tell the application to not follow anymore the profile""" if not profile in self.profiles: warning (_("This profile is not plugged")) return self.profiles.remove(profile) def clear_profile(self): self.profiles.clear() def connected(self, profile): """called when the connection is made""" if not self.check_profile(profile): return debug(_("Connected")) self.setStatusOnline(True) def disconnected(self, profile): """called when the connection is closed""" if not self.check_profile(profile): return debug(_("Disconnected")) self.CM.clear() self.contactList.clear_contacts() self.setStatusOnline(False) def connectionError(self, error_type, profile): """called when something goest wrong with the connection""" if not self.check_profile(profile): return debug(_("Connection Error")) self.disconnected(profile) if error_type == "AUTH_ERROR": self.showDialog(_("Can't connect to account, please check your password"), _("Connection error"), "error") else: error(_('FIXME: error_type %s not implemented') % error_type) def newContact(self, JabberId, attributes, groups, profile): if not self.check_profile(profile): return entity=JID(JabberId) _groups = list(groups) self.rosterList[entity.short]=(dict(attributes), _groups) if entity in self.CM: self.CM.update(entity, 'groups', _groups) self.contactList.replace(entity, self.CM.getAttr(entity, 'groups')) def newMessage(self, from_jid, msg, type, to_jid, profile): if not self.check_profile(profile): return sender=JID(from_jid) addr=JID(to_jid) win = addr if sender.short == self.profiles[profile]['whoami'].short else sender self.current_action_ids = set() self.current_action_ids_cb = {} self.chat_wins[win.short].printMessage(sender, msg, profile) def newAlert(self, msg, title, alert_type, profile): if not self.check_profile(profile): return assert alert_type in ['INFO','ERROR'] self.showDialog(unicode(msg),unicode(title),alert_type.lower()) def setStatusOnline(self, online=True): pass def presenceUpdate(self, jabber_id, show, priority, statuses, profile): if not self.check_profile(profile): return debug (_("presence update for %(jid)s (show=%(show)s, priority=%(priority)s, statuses=%(statuses)s) [profile:%(profile)s]") % {'jid':jabber_id, 'show':show, 'priority':priority, 'statuses':statuses, 'profile':profile}); from_jid=JID(jabber_id) debug ("from_jid.short=%(from_jid)s whoami.short=%(whoami)s" % {'from_jid':from_jid.short, 'whoami':self.profiles[profile]['whoami'].short}) if from_jid.short==self.profiles[profile]['whoami'].short: if not type: self.setStatusOnline(True) elif type=="unavailable": self.setStatusOnline(False) return if show != 'unavailable': name="" groups = [] if self.rosterList.has_key(from_jid.short): if self.rosterList[from_jid.short][0].has_key("name"): name=self.rosterList[from_jid.short][0]["name"] groups=self.rosterList[from_jid.short][1] #FIXME: must be moved in a plugin if from_jid.short in self.profiles[profile]['watched'] and not from_jid.short in self.profiles[profile]['onlineContact']: self.showAlert(_("Watched jid [%s] is connected !") % from_jid.short) self.profiles[profile]['onlineContact'].add(from_jid) #FIXME onlineContact is useless with CM, must be removed self.CM.add(from_jid) self.CM.update(from_jid, 'name', unicode(name)) self.CM.update(from_jid, 'show', show) self.CM.update(from_jid, 'statuses', statuses) self.CM.update(from_jid, 'groups', groups) cache = self.bridge.getCardCache(from_jid) if cache.has_key('nick'): self.CM.update(from_jid, 'nick', unicode(cache['nick'])) if cache.has_key('avatar'): self.CM.update(from_jid, 'avatar', self.bridge.getAvatarFile(cache['avatar'])) self.contactList.replace(from_jid, self.CM.getAttr(from_jid, 'groups')) if show=="unavailable" and from_jid in self.profiles[profile]['onlineContact']: self.profiles[profile]['onlineContact'].remove(from_jid) self.CM.remove(from_jid) if not self.CM.isConnected(from_jid): self.contactList.disconnect(from_jid) def roomJoined(self, room_id, room_service, room_nicks, user_nick, profile): """Called when a MUC room is joined""" if not self.check_profile(profile): return debug (_("Room [%(room_name)s] joined by %(profile)s, users presents:%(users)s") % {'room_name':room_id+'@'+room_service, 'profile': profile, 'users':room_nicks}) room_jid=room_id+'@'+room_service self.chat_wins[room_jid].setUserNick(user_nick) self.chat_wins[room_jid].setType("group") self.chat_wins[room_jid].id = room_jid self.chat_wins[room_jid].setPresents(list(set([user_nick]+room_nicks))) def roomUserJoined(self, room_id, room_service, user_nick, user_data, profile): """Called when an user joined a MUC room""" if not self.check_profile(profile): return room_jid=room_id+'@'+room_service if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].replaceUser(user_nick) debug (_("user [%(user_nick)s] joined room [%(room_jid)s]") % {'user_nick':user_nick, 'room_jid':room_jid}) def roomUserLeft(self, room_id, room_service, user_nick, user_data, profile): """Called when an user joined a MUC room""" if not self.check_profile(profile): return room_jid=room_id+'@'+room_service if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].removeUser(user_nick) debug (_("user [%(user_nick)s] left room [%(room_jid)s]") % {'user_nick':user_nick, 'room_jid':room_jid}) def roomNewSubject(self, room_id, room_service, subject, profile): """Called when subject of MUC room change""" if not self.check_profile(profile): return room_jid=room_id+'@'+room_service if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].setSubject(subject) debug (_("new subject for room [%(room_jid)s]: %(subject)s") % {'room_jid':room_jid, "subject":subject}) def tarotGameStarted(self, room_jid, referee, players, profile): if not self.check_profile(profile): return debug (_("Tarot Game Started \o/")) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].startGame("Tarot", referee, players) 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 tarotGameNew(self, room_jid, hand, profile): if not self.check_profile(profile): return debug (_("New Tarot Game")) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").newGame(hand) def tarotChooseContrat(self, room_jid, xml_data, profile): """Called when the player has to select his contrat""" if not self.check_profile(profile): return debug (_("Tarot: need to select a contrat")) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").chooseContrat(xml_data) def tarotShowCards(self, room_jid, game_stage, cards, data, profile): if not self.check_profile(profile): return debug (_("Show cards")) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").showCards(game_stage, cards, data) def tarotMyTurn(self, room_jid, profile): if not self.check_profile(profile): return debug (_("My turn to play")) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").myTurn() def tarotScore(self, room_jid, xml_data, winners, loosers, profile): """Called when the game is finished and the score are updated""" if not self.check_profile(profile): return debug (_("Tarot: score received")) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").showScores(xml_data, winners, loosers) def tarotCardsPlayed(self, room_jid, player, cards, profile): if not self.check_profile(profile): return debug (_("Card(s) played (%(player)s): %(cards)s") % {"player":player, "cards":cards}) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").cardsPlayed(player, cards) def tarotInvalidCards(self, room_jid, phase, played_cards, invalid_cards, profile): if not self.check_profile(profile): return debug (_("Cards played are not valid: %s") % invalid_cards) if self.chat_wins.has_key(room_jid): self.chat_wins[room_jid].getGame("Tarot").invalidCards(phase, played_cards, invalid_cards) def _subscribe_cb(self, answer, data): entity, profile = data if answer: self.bridge.subscription("subscribed", entity.short, profile_key = profile) else: self.bridge.subscription("unsubscribed", entity.short, profile_key = profile) def subscribe(self, type, raw_jid, profile): """Called when a subsciption management signal is received""" if not self.check_profile(profile): return entity = 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.short, _('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.short, _('Subscription refusal'), 'error') elif type=="subscribe": # this is a subscriptionn request, we have to ask for user confirmation answer = self.showDialog(_("The contact %s wants to subscribe to your presence.\nDo you accept ?") % entity.short, _('Subscription confirmation'), 'yes/no', answer_cb = self._subscribe_cb, answer_data=(entity, profile)) def showDialog(self, message, title, type="info", answer_cb = None): raise NotImplementedError def showAlert(self, message): pass #FIXME def paramUpdate(self, name, value, namespace, profile): if not self.check_profile(profile): return debug(_("param update: [%(namespace)s] %(name)s = %(value)s") % {'namespace':namespace, 'name':name, 'value':value}) if (namespace,name) == ("Connection", "JabberID"): debug (_("Changing JID to %s"), value) self.profiles[profile]['whoami']=JID(value) elif (namespace,name) == ("Misc", "Watched"): self.profiles[profile]['watched']=value.split() def contactDeleted(self, jid, profile): if not self.check_profile(profile): return target = JID(jid) self.contactList.remove(self.CM.get_full(target)) self.CM.remove(target) try: self.profiles[profile]['onlineContact'].remove(target.short) except KeyError: pass def updatedValue(self, name, data): if name == "card_nick": target = JID(data['jid']) if target in self.contactList: self.CM.update(target, 'nick', unicode(data['nick'])) self.contactList.replace(target) elif name == "card_avatar": target = JID(data['jid']) if target in self.contactList: filename = self.bridge.getAvatarFile(data['avatar']) self.CM.update(target, 'avatar', filename) self.contactList.replace(target) def askConfirmation(self, type, id, data): raise NotImplementedError def actionResult(self, type, id, data): raise NotImplementedError def onExit(self): """Must be called when the frontend is terminating""" #TODO: mange multi-profile here try: autodisconnect = self.bridge.getParamA("autodisconnect","Connection", profile_key=self.profile) == "true" if autodisconnect and self.bridge.isConnected(self.profile): #Does the user want autodisconnection ? self.bridge.disconnect(self.profile) except: pass