comparison sat/core/sat_main.py @ 3254:6cf4bd6972c2

core, frontends: avatar refactoring: /!\ huge commit Avatar logic has been reworked around the IDENTITY plugin: plugins able to handle avatar or other identity related metadata (like nicknames) register to IDENTITY plugin in the same way as for other features like download/upload. Once registered, IDENTITY plugin will call them when suitable in order of priority, and handle caching. Methods to manage those metadata from frontend now use serialised data. For now `avatar` and `nicknames` are handled: - `avatar` is now a dict with `path` + metadata like `media_type`, instead of just a string path - `nicknames` is now a list of nicknames in order of priority. This list is never empty, and `nicknames[0]` should be the preferred nickname to use by frontends in most cases. In addition to contact specified nicknames, user set nickname (the one set in roster) is used in priority when available. Among the side changes done with this commit, there are: - a new `contactGet` bridge method to get roster metadata for a single contact - SatPresenceProtocol.send returns a Deferred to check when it has actually been sent - memory's methods to handle entities data now use `client` as first argument - metadata filter can be specified with `getIdentity` - `getAvatar` and `setAvatar` are now part of the IDENTITY plugin instead of XEP-0054 (and there signature has changed) - `isRoom` and `getBareOrFull` are now part of XEP-0045 plugin - jp avatar/get command uses `xdg-open` first when available for `--show` flag - `--no-cache` has been added to jp avatar/get and identity/get - jp identity/set has been simplified, explicit options (`--nickname` only for now) are used instead of `--field`. `--field` may come back in the future if necessary for extra data. - QuickContactList `SetContact` now handle None as a value, and doesn't use it to delete the metadata anymore - improved cache handling for `metadata` and `nicknames` in quick frontend - new `default` argument in QuickContactList `getCache`
author Goffi <goffi@goffi.org>
date Tue, 14 Apr 2020 21:00:33 +0200
parents b0c57c9a4bd8
children f300d78f08f3
comparison
equal deleted inserted replaced
3253:1af840e84af7 3254:6cf4bd6972c2
100 self.bridge.register_method("getReady", lambda: self.initialised) 100 self.bridge.register_method("getReady", lambda: self.initialised)
101 self.bridge.register_method("getVersion", lambda: self.full_version) 101 self.bridge.register_method("getVersion", lambda: self.full_version)
102 self.bridge.register_method("getFeatures", self.getFeatures) 102 self.bridge.register_method("getFeatures", self.getFeatures)
103 self.bridge.register_method("profileNameGet", self.memory.getProfileName) 103 self.bridge.register_method("profileNameGet", self.memory.getProfileName)
104 self.bridge.register_method("profilesListGet", self.memory.getProfilesList) 104 self.bridge.register_method("profilesListGet", self.memory.getProfilesList)
105 self.bridge.register_method( 105 self.bridge.register_method("getEntityData", self.memory._getEntityData)
106 "getEntityData",
107 lambda jid_, keys, profile: self.memory.getEntityData(
108 jid.JID(jid_), keys, profile
109 ),
110 )
111 self.bridge.register_method("getEntitiesData", self.memory._getEntitiesData) 106 self.bridge.register_method("getEntitiesData", self.memory._getEntitiesData)
112 self.bridge.register_method("profileCreate", self.memory.createProfile) 107 self.bridge.register_method("profileCreate", self.memory.createProfile)
113 self.bridge.register_method("asyncDeleteProfile", self.memory.asyncDeleteProfile) 108 self.bridge.register_method("asyncDeleteProfile", self.memory.asyncDeleteProfile)
114 self.bridge.register_method("profileStartSession", self.memory.startSession) 109 self.bridge.register_method("profileStartSession", self.memory.startSession)
115 self.bridge.register_method( 110 self.bridge.register_method(
116 "profileIsSessionStarted", self.memory._isSessionStarted 111 "profileIsSessionStarted", self.memory._isSessionStarted
117 ) 112 )
118 self.bridge.register_method("profileSetDefault", self.memory.profileSetDefault) 113 self.bridge.register_method("profileSetDefault", self.memory.profileSetDefault)
119 self.bridge.register_method("connect", self._connect) 114 self.bridge.register_method("connect", self._connect)
120 self.bridge.register_method("disconnect", self.disconnect) 115 self.bridge.register_method("disconnect", self.disconnect)
116 self.bridge.register_method("contactGet", self._contactGet)
121 self.bridge.register_method("getContacts", self.getContacts) 117 self.bridge.register_method("getContacts", self.getContacts)
122 self.bridge.register_method("getContactsFromGroup", self.getContactsFromGroup) 118 self.bridge.register_method("getContactsFromGroup", self.getContactsFromGroup)
123 self.bridge.register_method("getMainResource", self.memory._getMainResource) 119 self.bridge.register_method("getMainResource", self.memory._getMainResource)
124 self.bridge.register_method( 120 self.bridge.register_method(
125 "getPresenceStatuses", self.memory._getPresenceStatuses 121 "getPresenceStatuses", self.memory._getPresenceStatuses
537 return ret 533 return ret
538 534
539 d_list.addCallback(buildFeatures, list(self.plugins.keys())) 535 d_list.addCallback(buildFeatures, list(self.plugins.keys()))
540 return d_list 536 return d_list
541 537
538 def _contactGet(self, entity_jid_s, profile_key):
539 client = self.getClient(profile_key)
540 entity_jid = jid.JID(entity_jid_s)
541 return defer.ensureDeferred(self.getContact(client, entity_jid))
542
543 async def getContact(self, client, entity_jid):
544 # we want to be sure that roster has been received
545 await client.roster.got_roster
546 item = client.roster.getItem(entity_jid)
547 if item is None:
548 raise exceptions.NotFound(f"{entity_jid} is not in roster!")
549 return (client.roster.getAttributes(item), list(item.groups))
550
542 def getContacts(self, profile_key): 551 def getContacts(self, profile_key):
543 client = self.getClient(profile_key) 552 client = self.getClient(profile_key)
544 553
545 def got_roster(__): 554 def got_roster(__):
546 ret = [] 555 ret = []
706 resources.add(client.jid.resource) 715 resources.add(client.jid.resource)
707 ret_data = [] 716 ret_data = []
708 for resource in resources: 717 for resource in resources:
709 res_jid = copy.copy(bare_jid) 718 res_jid = copy.copy(bare_jid)
710 res_jid.resource = resource 719 res_jid.resource = resource
711 cache_data = self.memory.getEntityData(res_jid, profile_key=client.profile) 720 cache_data = self.memory.getEntityData(client, res_jid)
712 res_data = { 721 res_data = {
713 "resource": resource, 722 "resource": resource,
714 } 723 }
715 try: 724 try:
716 presence = cache_data['presence'] 725 presence = cache_data['presence']
955 # presence is sufficient, as a roster push will be sent according to 964 # presence is sufficient, as a roster push will be sent according to
956 # RFC 6121 §3.1.2 965 # RFC 6121 §3.1.2
957 self.profiles[profile].presence.subscribe(to_jid) 966 self.profiles[profile].presence.subscribe(to_jid)
958 967
959 def _updateContact(self, to_jid_s, name, groups, profile_key): 968 def _updateContact(self, to_jid_s, name, groups, profile_key):
960 return self.updateContact(jid.JID(to_jid_s), name, groups, profile_key) 969 client = self.getClient(profile_key)
961 970 return self.updateContact(client, jid.JID(to_jid_s), name, groups)
962 def updateContact(self, to_jid, name, groups, profile_key): 971
972 def updateContact(self, client, to_jid, name, groups):
963 """update a contact in roster list""" 973 """update a contact in roster list"""
964 profile = self.memory.getProfileName(profile_key)
965 assert profile
966 groups = set(groups)
967 roster_item = RosterItem(to_jid) 974 roster_item = RosterItem(to_jid)
968 roster_item.name = name or None 975 roster_item.name = name or u''
969 roster_item.groups = set(groups) 976 roster_item.groups = set(groups)
970 return self.profiles[profile].roster.setItem(roster_item) 977 if not self.trigger.point("roster_update", client, roster_item):
978 return
979 return client.roster.setItem(roster_item)
971 980
972 def _delContact(self, to_jid_s, profile_key): 981 def _delContact(self, to_jid_s, profile_key):
973 return self.delContact(jid.JID(to_jid_s), profile_key) 982 return self.delContact(jid.JID(to_jid_s), profile_key)
974 983
975 def delContact(self, to_jid, profile_key): 984 def delContact(self, to_jid, profile_key):