Mercurial > libervia-backend
comparison sat_frontends/quick_frontend/quick_chat.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 | 2556eb576aed |
children | be6d91572633 |
comparison
equal
deleted
inserted
replaced
3253:1af840e84af7 | 3254:6cf4bd6972c2 |
---|---|
26 from sat_frontends.tools import jid | 26 from sat_frontends.tools import jid |
27 import time | 27 import time |
28 | 28 |
29 | 29 |
30 log = getLogger(__name__) | 30 log = getLogger(__name__) |
31 | |
32 try: | |
33 from locale import getlocale | |
34 except ImportError: | |
35 # FIXME: pyjamas workaround | |
36 getlocale = lambda x: (None, "utf-8") | |
37 | 31 |
38 | 32 |
39 ROOM_USER_JOINED = "ROOM_USER_JOINED" | 33 ROOM_USER_JOINED = "ROOM_USER_JOINED" |
40 ROOM_USER_LEFT = "ROOM_USER_LEFT" | 34 ROOM_USER_LEFT = "ROOM_USER_LEFT" |
41 ROOM_USER_MOVED = (ROOM_USER_JOINED, ROOM_USER_LEFT) | 35 ROOM_USER_MOVED = (ROOM_USER_JOINED, ROOM_USER_LEFT) |
144 time_format = "%c" if timestamp < self.parent.day_change else "%H:%M" | 138 time_format = "%c" if timestamp < self.parent.day_change else "%H:%M" |
145 return time.strftime(time_format, timestamp) | 139 return time.strftime(time_format, timestamp) |
146 | 140 |
147 @property | 141 @property |
148 def avatar(self): | 142 def avatar(self): |
149 """avatar full path or None if no avatar is found""" | 143 """avatar data or None if no avatar is found""" |
150 ret = self.host.getAvatar(self.from_jid, profile=self.profile) | 144 entity = self.from_jid |
151 return ret | 145 contact_list = self.host.contact_lists[self.profile] |
146 try: | |
147 return contact_list.getCache(entity, "avatar") | |
148 except (exceptions.NotFound, KeyError): | |
149 # we don't check the result as the avatar listener will be called | |
150 self.host.bridge.avatarGet(entity, True, self.profile) | |
151 return None | |
152 | 152 |
153 @property | 153 @property |
154 def encrypted(self): | 154 def encrypted(self): |
155 return self.extra.get("encrypted", False) | 155 return self.extra.get("encrypted", False) |
156 | 156 |
167 if self.parent.type == C.CHAT_GROUP or entity in list( | 167 if self.parent.type == C.CHAT_GROUP or entity in list( |
168 contact_list.getSpecials(C.CONTACT_SPECIAL_GROUP) | 168 contact_list.getSpecials(C.CONTACT_SPECIAL_GROUP) |
169 ): | 169 ): |
170 return entity.resource or "" | 170 return entity.resource or "" |
171 if entity.bare in contact_list: | 171 if entity.bare in contact_list: |
172 return ( | 172 |
173 contact_list.getCache(entity, "nick") | 173 try: |
174 or contact_list.getCache(entity, "name") | 174 nicknames = contact_list.getCache(entity, "nicknames") |
175 or entity.node | 175 except (exceptions.NotFound, KeyError): |
176 or entity | 176 # we check result as listener will be called |
177 ) | 177 self.host.bridge.identityGet( |
178 entity.bare, ["nicknames"], True, self.profile) | |
179 return entity.node or entity | |
180 | |
181 if nicknames: | |
182 return nicknames[0] | |
183 else: | |
184 return ( | |
185 contact_list.getCache(entity, "name", default=None) | |
186 or entity.node | |
187 or entity | |
188 ) | |
189 | |
178 return entity.node or entity | 190 return entity.node or entity |
179 | 191 |
180 @property | 192 @property |
181 def status(self): | 193 def status(self): |
182 return self._status | 194 return self._status |
212 @property | 224 @property |
213 def attachments(self): | 225 def attachments(self): |
214 return self.extra.get(C.MESS_KEY_ATTACHMENTS) | 226 return self.extra.get(C.MESS_KEY_ATTACHMENTS) |
215 | 227 |
216 | 228 |
217 class MessageWidget(object): | 229 class MessageWidget: |
218 """Base classe for widgets""" | 230 """Base classe for widgets""" |
219 # This class does nothing and is only used to have a common ancestor | 231 # This class does nothing and is only used to have a common ancestor |
220 | 232 |
221 pass | 233 pass |
222 | 234 |
223 | 235 |
224 class Occupant(object): | 236 class Occupant: |
225 """Occupant metadata""" | 237 """Occupant metadata""" |
226 | 238 |
227 def __init__(self, parent, data, profile): | 239 def __init__(self, parent, data, profile): |
228 self.parent = parent | 240 self.parent = parent |
229 self.profile = profile | 241 self.profile = profile |
614 log_msg += _(" ({} messages)".format(size)) | 626 log_msg += _(" ({} messages)".format(size)) |
615 log.debug(log_msg) | 627 log.debug(log_msg) |
616 | 628 |
617 if self.type == C.CHAT_ONE2ONE: | 629 if self.type == C.CHAT_ONE2ONE: |
618 special = self.host.contact_lists[self.profile].getCache( | 630 special = self.host.contact_lists[self.profile].getCache( |
619 self.target, C.CONTACT_SPECIAL, create_if_not_found=True | 631 self.target, C.CONTACT_SPECIAL, create_if_not_found=True, default=None |
620 ) | 632 ) |
621 if special == C.CONTACT_SPECIAL_GROUP: | 633 if special == C.CONTACT_SPECIAL_GROUP: |
622 # we have a private conversation | 634 # we have a private conversation |
623 # so we need full jid for the history | 635 # so we need full jid for the history |
624 # (else we would get history from group itself) | 636 # (else we would get history from group itself) |
898 except KeyError: | 910 except KeyError: |
899 pass | 911 pass |
900 else: | 912 else: |
901 mess_data.status = status | 913 mess_data.status = status |
902 | 914 |
903 def onAvatar(self, entity, filename, profile): | 915 def onAvatar(self, entity, avatar_data, profile): |
904 if self.type == C.CHAT_GROUP: | 916 if self.type == C.CHAT_GROUP: |
905 if entity.bare == self.target: | 917 if entity.bare == self.target: |
906 try: | 918 try: |
907 self.occupants[entity.resource].update({"avatar": filename}) | 919 self.occupants[entity.resource].update({"avatar": avatar_data}) |
908 except KeyError: | 920 except KeyError: |
909 # can happen for a message in history where the | 921 # can happen for a message in history where the |
910 # entity is not here anymore | 922 # entity is not here anymore |
911 pass | 923 pass |
912 | 924 |
913 for m in list(self.messages.values()): | 925 for m in list(self.messages.values()): |
914 if m.nick == entity.resource: | 926 if m.nick == entity.resource: |
915 for w in m.widgets: | 927 for w in m.widgets: |
916 w.update({"avatar": filename}) | 928 w.update({"avatar": avatar_data}) |
917 else: | 929 else: |
918 if ( | 930 if ( |
919 entity.bare == self.target.bare | 931 entity.bare == self.target.bare |
920 or entity.bare == self.host.profiles[profile].whoami.bare | 932 or entity.bare == self.host.profiles[profile].whoami.bare |
921 ): | 933 ): |
922 log.info("avatar updated for {}".format(entity)) | 934 log.info("avatar updated for {}".format(entity)) |
923 for m in list(self.messages.values()): | 935 for m in list(self.messages.values()): |
924 if m.from_jid.bare == entity.bare: | 936 if m.from_jid.bare == entity.bare: |
925 for w in m.widgets: | 937 for w in m.widgets: |
926 w.update({"avatar": filename}) | 938 w.update({"avatar": avatar_data}) |
927 | 939 |
928 | 940 |
929 quick_widgets.register(QuickChat) | 941 quick_widgets.register(QuickChat) |