2 ## src/common/contacts.py
4 ## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
5 ## Travis Shirk <travis AT pobox.com>
6 ## Nikos Kouremenos <kourem AT gmail.com>
7 ## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
8 ## Jean-Marie Traissard <jim AT lapin.org>
9 ## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
10 ## Tomasz Melcer <liori AT exroot.org>
11 ## Julien Pivotto <roidelapluie AT gmail.com>
12 ## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
13 ## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
14 ## Jonathan Schleifer <js-gajim AT webkeks.org>
16 ## This file is part of Gajim.
18 ## Gajim is free software; you can redistribute it and/or modify
19 ## it under the terms of the GNU General Public License as published
20 ## by the Free Software Foundation; version 3 only.
22 ## Gajim is distributed in the hope that it will be useful,
23 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ## GNU General Public License for more details.
27 ## You should have received a copy of the GNU General Public License
28 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
32 from common
import caps_cache
33 from common
.account
import Account
36 class XMPPEntity(object):
38 Base representation of entities in XMPP
41 def __init__(self
, jid
, account
, resource
):
43 self
.resource
= resource
44 self
.account
= account
46 class CommonContact(XMPPEntity
):
48 def __init__(self
, jid
, account
, resource
, show
, status
, name
,
49 our_chatstate
, composing_xep
, chatstate
, client_caps
=None):
51 XMPPEntity
.__init
__(self
, jid
, account
, resource
)
57 self
.client_caps
= client_caps
or caps_cache
.NullClientCaps()
59 # please read xep-85 http://www.xmpp.org/extensions/xep-0085.html
60 # we keep track of xep85 support with the peer by three extra states:
61 # None, False and 'ask'
62 # None if no info about peer
63 # False if peer does not support xep85
64 # 'ask' if we sent the first 'active' chatstate and are waiting for reply
65 # this holds what WE SEND to contact (our current chatstate)
66 self
.our_chatstate
= our_chatstate
67 # tell which XEP we're using for composing state
68 # None = have to ask, XEP-0022 = use this xep,
69 # XEP-0085 = use this xep, False = no composing support
70 self
.composing_xep
= composing_xep
71 # this is contact's chatstate
72 self
.chatstate
= chatstate
74 def get_full_jid(self
):
75 raise NotImplementedError
77 def get_shown_name(self
):
78 raise NotImplementedError
80 def supports(self
, requested_feature
):
82 Return True if the contact has advertised to support the feature
83 identified by the given namespace. False otherwise.
85 if self
.show
== 'offline':
86 # Unfortunately, if all resources are offline, the contact
87 # includes the last resource that was online. Check for its
88 # show, so we can be sure it's existant. Otherwise, we still
89 # return caps for a contact that has no resources left.
92 return caps_cache
.client_supports(self
.client_caps
, requested_feature
)
95 class Contact(CommonContact
):
97 Information concerning a contact
99 def __init__(self
, jid
, account
, name
='', groups
=[], show
='', status
='',
100 sub
='', ask
='', resource
='', priority
=0, keyID
='', client_caps
=None,
101 our_chatstate
=None, chatstate
=None, last_status_time
=None, msg_id
=
102 None, composing_xep
=None, last_activity_time
=None):
104 CommonContact
.__init
__(self
, jid
, account
, resource
, show
, status
, name
,
105 our_chatstate
, composing_xep
, chatstate
, client_caps
=client_caps
)
107 self
.contact_name
= '' # nick choosen by contact
108 self
.groups
= [i
for i
in set(groups
)] # filter duplicate values
113 self
.priority
= priority
116 self
.last_status_time
= last_status_time
117 self
.last_activity_time
= last_activity_time
121 def get_full_jid(self
):
123 return self
.jid
+ '/' + self
.resource
126 def get_shown_name(self
):
129 if self
.contact_name
:
130 return self
.contact_name
131 return self
.jid
.split('@')[0]
133 def get_shown_groups(self
):
134 if self
.is_observer():
135 return [_('Observers')]
136 elif self
.is_groupchat():
137 return [_('Groupchats')]
138 elif self
.is_transport():
139 return [_('Transports')]
140 elif not self
.groups
:
141 return [_('General')]
145 def is_hidden_from_roster(self
):
147 If contact should not be visible in roster
149 # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html
150 if self
.is_transport():
152 if self
.sub
in ('both', 'to'):
154 if self
.sub
in ('none', 'from') and self
.ask
== 'subscribe':
156 if self
.sub
in ('none', 'from') and (self
.name
or len(self
.groups
)):
158 if _('Not in Roster') in self
.groups
:
162 def is_observer(self
):
163 # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html
165 if self
.sub
== 'from' and not self
.is_transport()\
166 and self
.is_hidden_from_roster():
170 def is_groupchat(self
):
171 for account
in common
.gajim
.gc_connected
:
172 if self
.jid
in common
.gajim
.gc_connected
[account
]:
176 def is_transport(self
):
177 # if not '@' or '@' starts the jid then contact is transport
178 return self
.jid
.find('@') <= 0
181 class GC_Contact(CommonContact
):
183 Information concerning each groupchat contact
186 def __init__(self
, room_jid
, account
, name
='', show
='', status
='', role
='',
187 affiliation
='', jid
='', resource
='', our_chatstate
=None,
188 composing_xep
=None, chatstate
=None):
190 CommonContact
.__init
__(self
, jid
, account
, resource
, show
, status
, name
,
191 our_chatstate
, composing_xep
, chatstate
)
193 self
.room_jid
= room_jid
195 self
.affiliation
= affiliation
197 def get_full_jid(self
):
198 return self
.room_jid
+ '/' + self
.name
200 def get_shown_name(self
):
203 def as_contact(self
):
205 Create a Contact instance from this GC_Contact instance
207 return Contact(jid
=self
.get_full_jid(), account
=self
.account
,
208 name
=self
.name
, groups
=[], show
=self
.show
, status
=self
.status
,
209 sub
='none', client_caps
=self
.client_caps
)
212 class LegacyContactsAPI
:
214 This is a GOD class for accessing contact and groupchat information.
215 The API has several flaws:
217 * it mixes concerns because it deals with contacts, groupchats,
218 groupchat contacts and metacontacts
219 * some methods like get_contact() may return None. This leads to
220 a lot of duplication all over Gajim because it is not sure
221 if we receive a proper contact or just None.
223 It is a long way to cleanup this API. Therefore just stick with it
224 and use it as before. We will try to figure out a migration path.
227 self
._metacontact
_manager
= MetacontactManager(self
)
230 def change_account_name(self
, old_name
, new_name
):
231 self
._accounts
[new_name
] = self
._accounts
[old_name
]
232 self
._accounts
[new_name
].name
= new_name
233 del self
._accounts
[old_name
]
235 self
._metacontact
_manager
.change_account_name(old_name
, new_name
)
237 def add_account(self
, account_name
):
238 self
._accounts
[account_name
] = Account(account_name
, Contacts(),
240 self
._metacontact
_manager
.add_account(account_name
)
242 def get_accounts(self
):
243 return self
._accounts
.keys()
245 def remove_account(self
, account
):
246 del self
._accounts
[account
]
247 self
._metacontact
_manager
.remove_account(account
)
249 def create_contact(self
, jid
, account
, name
='', groups
=[], show
='',
250 status
='', sub
='', ask
='', resource
='', priority
=0, keyID
='',
251 client_caps
=None, our_chatstate
=None, chatstate
=None, last_status_time
=None,
252 composing_xep
=None, last_activity_time
=None):
253 # Use Account object if available
254 account
= self
._accounts
.get(account
, account
)
255 return Contact(jid
=jid
, account
=account
, name
=name
, groups
=groups
,
256 show
=show
, status
=status
, sub
=sub
, ask
=ask
, resource
=resource
,
257 priority
=priority
, keyID
=keyID
, client_caps
=client_caps
,
258 our_chatstate
=our_chatstate
, chatstate
=chatstate
,
259 last_status_time
=last_status_time
, composing_xep
=composing_xep
,
260 last_activity_time
=last_activity_time
)
262 def create_self_contact(self
, jid
, account
, resource
, show
, status
, priority
,
264 conn
= common
.gajim
.connections
[account
]
265 nick
= name
or common
.gajim
.nicks
[account
]
266 account
= self
._accounts
.get(account
, account
) # Use Account object if available
267 self_contact
= self
.create_contact(jid
=jid
, account
=account
,
268 name
=nick
, groups
=['self_contact'], show
=show
, status
=status
,
269 sub
='both', ask
='none', priority
=priority
, keyID
=keyID
,
271 self_contact
.pep
= conn
.pep
274 def create_not_in_roster_contact(self
, jid
, account
, resource
='', name
='', keyID
=''):
275 account
= self
._accounts
.get(account
, account
) # Use Account object if available
276 return self
.create_contact(jid
=jid
, account
=account
, resource
=resource
,
277 name
=name
, groups
=[_('Not in Roster')], show
='not in roster',
278 status
='', sub
='none', keyID
=keyID
)
280 def copy_contact(self
, contact
):
281 return self
.create_contact(contact
.jid
, contact
.account
,
282 name
=contact
.name
, groups
=contact
.groups
, show
=contact
.show
,
283 status
=contact
.status
, sub
=contact
.sub
, ask
=contact
.ask
,
284 resource
=contact
.resource
, priority
=contact
.priority
,
285 keyID
=contact
.keyID
, client_caps
=contact
.client_caps
,
286 our_chatstate
=contact
.our_chatstate
, chatstate
=contact
.chatstate
,
287 last_status_time
=contact
.last_status_time
,
288 composing_xep
=contact
.composing_xep
,
289 last_activity_time
=contact
.last_activity_time
)
291 def add_contact(self
, account
, contact
):
292 if account
not in self
._accounts
:
293 self
.add_account(account
)
294 return self
._accounts
[account
].contacts
.add_contact(contact
)
296 def remove_contact(self
, account
, contact
):
297 if account
not in self
._accounts
:
299 return self
._accounts
[account
].contacts
.remove_contact(contact
)
301 def remove_jid(self
, account
, jid
, remove_meta
=True):
302 self
._accounts
[account
].contacts
.remove_jid(jid
)
304 self
._metacontact
_manager
.remove_metacontact(account
, jid
)
306 def get_contacts(self
, account
, jid
):
307 return self
._accounts
[account
].contacts
.get_contacts(jid
)
309 def get_contact(self
, account
, jid
, resource
=None):
310 return self
._accounts
[account
].contacts
.get_contact(jid
, resource
=resource
)
312 def iter_contacts(self
, account
):
313 for contact
in self
._accounts
[account
].contacts
.iter_contacts():
316 def get_contact_from_full_jid(self
, account
, fjid
):
317 return self
._accounts
[account
].contacts
.get_contact_from_full_jid(fjid
)
319 def get_first_contact_from_jid(self
, account
, jid
):
320 return self
._accounts
[account
].contacts
.get_first_contact_from_jid(jid
)
322 def get_contacts_from_group(self
, account
, group
):
323 return self
._accounts
[account
].contacts
.get_contacts_from_group(group
)
325 def get_contacts_jid_list(self
, account
):
326 return self
._accounts
[account
].contacts
.get_contacts_jid_list()
328 def get_jid_list(self
, account
):
329 return self
._accounts
[account
].contacts
.get_jid_list()
331 def change_contact_jid(self
, old_jid
, new_jid
, account
):
332 return self
._accounts
[account
].change_contact_jid(old_jid
, new_jid
)
334 def get_highest_prio_contact_from_contacts(self
, contacts
):
337 prim_contact
= contacts
[0]
338 for contact
in contacts
[1:]:
339 if int(contact
.priority
) > int(prim_contact
.priority
):
340 prim_contact
= contact
343 def get_contact_with_highest_priority(self
, account
, jid
):
344 contacts
= self
.get_contacts(account
, jid
)
345 if not contacts
and '/' in jid
:
346 # jid may be a fake jid, try it
347 room
, nick
= jid
.split('/', 1)
348 contact
= self
.get_gc_contact(account
, room
, nick
)
350 return self
.get_highest_prio_contact_from_contacts(contacts
)
352 def get_nb_online_total_contacts(self
, accounts
=[], groups
=[]):
354 Return the number of online contacts and the total number of contacts
357 accounts
= self
.get_accounts()
360 for account
in accounts
:
361 our_jid
= common
.gajim
.get_jid_from_account(account
)
362 for jid
in self
.get_jid_list(account
):
365 if common
.gajim
.jid_is_transport(jid
) and not \
366 _('Transports') in groups
:
367 # do not count transports
369 if self
.has_brother(account
, jid
, accounts
) and not \
370 self
.is_big_brother(account
, jid
, accounts
):
371 # count metacontacts only once
373 contact
= self
.get_contact_with_highest_priority(account
, jid
)
374 if _('Not in roster') in contact
.groups
:
381 if group
in contact
.get_shown_groups():
386 if contact
.show
not in ('offline', 'error'):
389 return nbr_online
, nbr_total
391 def __getattr__(self
, attr_name
):
392 # Only called if self has no attr_name
393 if hasattr(self
._metacontact
_manager
, attr_name
):
394 return getattr(self
._metacontact
_manager
, attr_name
)
396 raise AttributeError(attr_name
)
398 def create_gc_contact(self
, room_jid
, account
, name
='', show
='', status
='',
399 role
='', affiliation
='', jid
='', resource
=''):
400 account
= self
._accounts
.get(account
, account
) # Use Account object if available
401 return GC_Contact(room_jid
, account
, name
, show
, status
, role
, affiliation
, jid
,
404 def add_gc_contact(self
, account
, gc_contact
):
405 return self
._accounts
[account
].gc_contacts
.add_gc_contact(gc_contact
)
407 def remove_gc_contact(self
, account
, gc_contact
):
408 return self
._accounts
[account
].gc_contacts
.remove_gc_contact(gc_contact
)
410 def remove_room(self
, account
, room_jid
):
411 return self
._accounts
[account
].gc_contacts
.remove_room(room_jid
)
413 def get_gc_list(self
, account
):
414 return self
._accounts
[account
].gc_contacts
.get_gc_list()
416 def get_nick_list(self
, account
, room_jid
):
417 return self
._accounts
[account
].gc_contacts
.get_nick_list(room_jid
)
419 def get_gc_contact(self
, account
, room_jid
, nick
):
420 return self
._accounts
[account
].gc_contacts
.get_gc_contact(room_jid
, nick
)
422 def get_nb_role_total_gc_contacts(self
, account
, room_jid
, role
):
423 return self
._accounts
[account
].gc_contacts
.get_nb_role_total_gc_contacts(room_jid
, role
)
428 This is a breakout of the contact related behavior of the old
429 Contacts class (which is not called LegacyContactsAPI)
432 # list of contacts {jid1: [C1, C2]}, } one Contact per resource
435 def add_contact(self
, contact
):
436 if contact
.jid
not in self
._contacts
:
437 self
._contacts
[contact
.jid
] = [contact
]
439 contacts
= self
._contacts
[contact
.jid
]
440 # We had only one that was offline, remove it
441 if len(contacts
) == 1 and contacts
[0].show
== 'offline':
442 # Do not use self.remove_contact: it deteles
443 # self._contacts[account][contact.jid]
444 contacts
.remove(contacts
[0])
445 # If same JID with same resource already exists, use the new one
447 if c
.resource
== contact
.resource
:
448 self
.remove_contact(c
)
450 contacts
.append(contact
)
452 def remove_contact(self
, contact
):
453 if contact
.jid
not in self
._contacts
:
455 if contact
in self
._contacts
[contact
.jid
]:
456 self
._contacts
[contact
.jid
].remove(contact
)
457 if len(self
._contacts
[contact
.jid
]) == 0:
458 del self
._contacts
[contact
.jid
]
460 def remove_jid(self
, jid
):
462 Remove all contacts for a given jid
464 if jid
in self
._contacts
:
465 del self
._contacts
[jid
]
467 def get_contacts(self
, jid
):
469 Return the list of contact instances for this jid
471 return self
._contacts
.get(jid
, [])
473 def get_contact(self
, jid
, resource
=None):
475 # This function returns a *RANDOM* resource if resource = None!
476 # Do *NOT* use if you need to get the contact to which you
477 # send a message for example, as a bare JID in Jabber means
478 # highest available resource, which this function ignores!
480 Return the contact instance for the given resource if it's given else the
481 first contact is no resource is given or None if there is not
483 if jid
in self
._contacts
:
485 return self
._contacts
[jid
][0]
486 for c
in self
._contacts
[jid
]:
487 if c
.resource
== resource
:
490 def iter_contacts(self
):
491 for jid
in self
._contacts
.keys():
492 for contact
in self
._contacts
[jid
][:]:
495 def get_jid_list(self
):
496 return self
._contacts
.keys()
498 def get_contacts_jid_list(self
):
499 return [jid
for jid
, contact
in self
._contacts
.iteritems() if not
500 contact
[0].is_groupchat()]
502 def get_contact_from_full_jid(self
, fjid
):
504 Get Contact object for specific resource of given jid
506 barejid
, resource
= common
.gajim
.get_room_and_nick_from_fjid(fjid
)
507 return self
.get_contact(barejid
, resource
)
509 def get_first_contact_from_jid(self
, jid
):
510 if jid
in self
._contacts
:
511 return self
._contacts
[jid
][0]
513 def get_contacts_from_group(self
, group
):
515 Return all contacts in the given group
518 for jid
in self
._contacts
:
519 contacts
= self
.get_contacts(jid
)
520 if group
in contacts
[0].groups
:
521 group_contacts
+= contacts
522 return group_contacts
524 def change_contact_jid(self
, old_jid
, new_jid
):
525 if old_jid
not in self
._contacts
:
527 self
._contacts
[new_jid
] = []
528 for _contact
in self
._contacts
[old_jid
]:
529 _contact
.jid
= new_jid
530 self
._contacts
[new_jid
].append(_contact
)
531 del self
._contacts
[old_jid
]
537 # list of contacts that are in gc {room_jid: {nick: C}}}
540 def add_gc_contact(self
, gc_contact
):
541 if gc_contact
.room_jid
not in self
._rooms
:
542 self
._rooms
[gc_contact
.room_jid
] = {gc_contact
.name
: gc_contact
}
544 self
._rooms
[gc_contact
.room_jid
][gc_contact
.name
] = gc_contact
546 def remove_gc_contact(self
, gc_contact
):
547 if gc_contact
.room_jid
not in self
._rooms
:
549 if gc_contact
.name
not in self
._rooms
[gc_contact
.room_jid
]:
551 del self
._rooms
[gc_contact
.room_jid
][gc_contact
.name
]
552 # It was the last nick in room ?
553 if not len(self
._rooms
[gc_contact
.room_jid
]):
554 del self
._rooms
[gc_contact
.room_jid
]
556 def remove_room(self
, room_jid
):
557 if room_jid
in self
._rooms
:
558 del self
._rooms
[room_jid
]
560 def get_gc_list(self
):
561 return self
._rooms
.keys()
563 def get_nick_list(self
, room_jid
):
564 gc_list
= self
.get_gc_list()
565 if not room_jid
in gc_list
:
567 return self
._rooms
[room_jid
].keys()
569 def get_gc_contact(self
, room_jid
, nick
):
570 nick_list
= self
.get_nick_list(room_jid
)
571 if not nick
in nick_list
:
573 return self
._rooms
[room_jid
][nick
]
575 def get_nb_role_total_gc_contacts(self
, room_jid
, role
):
577 Return the number of group chat contacts for the given role and the total
578 number of group chat contacts
580 if room_jid
not in self
._rooms
:
582 nb_role
= nb_total
= 0
583 for nick
in self
._rooms
[room_jid
]:
584 if self
._rooms
[room_jid
][nick
].role
== role
:
587 return nb_role
, nb_total
590 class MetacontactManager():
592 def __init__(self
, contacts
):
593 self
._metacontacts
_tags
= {}
594 self
._contacts
= contacts
596 def change_account_name(self
, old_name
, new_name
):
597 self
._metacontacts
_tags
[new_name
] = self
._metacontacts
_tags
[old_name
]
598 del self
._metacontacts
_tags
[old_name
]
600 def add_account(self
, account
):
601 if account
not in self
._metacontacts
_tags
:
602 self
._metacontacts
_tags
[account
] = {}
604 def remove_account(self
, account
):
605 del self
._metacontacts
_tags
[account
]
607 def define_metacontacts(self
, account
, tags_list
):
608 self
._metacontacts
_tags
[account
] = tags_list
610 def _get_new_metacontacts_tag(self
, jid
):
611 if not jid
in self
._metacontacts
_tags
:
613 #FIXME: can this append ?
616 def iter_metacontacts_families(self
, account
):
617 for tag
in self
._metacontacts
_tags
[account
]:
618 family
= self
._get
_metacontacts
_family
_from
_tag
(account
, tag
)
621 def _get_metacontacts_tag(self
, account
, jid
):
623 Return the tag of a jid
625 if not account
in self
._metacontacts
_tags
:
627 for tag
in self
._metacontacts
_tags
[account
]:
628 for data
in self
._metacontacts
_tags
[account
][tag
]:
629 if data
['jid'] == jid
:
633 def add_metacontact(self
, brother_account
, brother_jid
, account
, jid
, order
=None):
634 tag
= self
._get
_metacontacts
_tag
(brother_account
, brother_jid
)
636 tag
= self
._get
_new
_metacontacts
_tag
(brother_jid
)
637 self
._metacontacts
_tags
[brother_account
][tag
] = [{'jid': brother_jid
,
639 if brother_account
!= account
:
640 common
.gajim
.connections
[brother_account
].store_metacontacts(
641 self
._metacontacts
_tags
[brother_account
])
642 # be sure jid has no other tag
643 old_tag
= self
._get
_metacontacts
_tag
(account
, jid
)
645 self
.remove_metacontact(account
, jid
)
646 old_tag
= self
._get
_metacontacts
_tag
(account
, jid
)
647 if tag
not in self
._metacontacts
_tags
[account
]:
648 self
._metacontacts
_tags
[account
][tag
] = [{'jid': jid
, 'tag': tag
}]
651 self
._metacontacts
_tags
[account
][tag
].append({'jid': jid
,
652 'tag': tag
, 'order': order
})
654 self
._metacontacts
_tags
[account
][tag
].append({'jid': jid
,
656 common
.gajim
.connections
[account
].store_metacontacts(
657 self
._metacontacts
_tags
[account
])
659 def remove_metacontact(self
, account
, jid
):
660 if not account
in self
._metacontacts
_tags
:
664 for tag
in self
._metacontacts
_tags
[account
]:
665 for data
in self
._metacontacts
_tags
[account
][tag
]:
666 if data
['jid'] == jid
:
670 self
._metacontacts
_tags
[account
][tag
].remove(found
)
671 common
.gajim
.connections
[account
].store_metacontacts(
672 self
._metacontacts
_tags
[account
])
675 def has_brother(self
, account
, jid
, accounts
):
676 tag
= self
._get
_metacontacts
_tag
(account
, jid
)
679 meta_jids
= self
._get
_metacontacts
_jids
(tag
, accounts
)
680 return len(meta_jids
) > 1 or len(meta_jids
[account
]) > 1
682 def is_big_brother(self
, account
, jid
, accounts
):
683 family
= self
.get_metacontacts_family(account
, jid
)
685 nearby_family
= [data
for data
in family
686 if account
in accounts
]
687 bb_data
= self
._get
_metacontacts
_big
_brother
(nearby_family
)
688 if bb_data
['jid'] == jid
and bb_data
['account'] == account
:
692 def _get_metacontacts_jids(self
, tag
, accounts
):
694 Return all jid for the given tag in the form {acct: [jid1, jid2],.}
697 for account
in self
._metacontacts
_tags
:
698 if tag
in self
._metacontacts
_tags
[account
]:
699 if account
not in accounts
:
701 answers
[account
] = []
702 for data
in self
._metacontacts
_tags
[account
][tag
]:
703 answers
[account
].append(data
['jid'])
706 def get_metacontacts_family(self
, account
, jid
):
708 Return the family of the given jid, including jid in the form:
709 [{'account': acct, 'jid': jid, 'order': order}, ] 'order' is optional
711 tag
= self
._get
_metacontacts
_tag
(account
, jid
)
712 return self
._get
_metacontacts
_family
_from
_tag
(account
, tag
)
714 def _get_metacontacts_family_from_tag(self
, account
, tag
):
718 for account
in self
._metacontacts
_tags
:
719 if tag
in self
._metacontacts
_tags
[account
]:
720 for data
in self
._metacontacts
_tags
[account
][tag
]:
721 data
['account'] = account
725 def _compare_metacontacts(self
, data1
, data2
):
727 Compare 2 metacontacts
729 Data is {'jid': jid, 'account': account, 'order': order} order is
734 account1
= data1
['account']
735 account2
= data2
['account']
736 contact1
= self
._contacts
.get_contact_with_highest_priority(account1
, jid1
)
737 contact2
= self
._contacts
.get_contact_with_highest_priority(account2
, jid2
)
738 show_list
= ['not in roster', 'error', 'offline', 'invisible', 'dnd',
739 'xa', 'away', 'chat', 'online', 'requested', 'message']
740 # contact can be null when a jid listed in the metacontact data
741 # is not in our roster
744 return -1 # prefer the known contact
749 show1
= show_list
.index(contact1
.show
)
750 priority1
= contact1
.priority
753 return 1 # prefer the known contact
758 show2
= show_list
.index(contact2
.show
)
759 priority2
= contact2
.priority
760 # If only one is offline, it's always second
761 if show1
> 2 and show2
< 3:
763 if show2
> 2 and show1
< 3:
765 if 'order' in data1
and 'order' in data2
:
766 if data1
['order'] > data2
['order']:
768 if data1
['order'] < data2
['order']:
774 transport1
= common
.gajim
.get_transport_name_from_jid(jid1
)
775 transport2
= common
.gajim
.get_transport_name_from_jid(jid2
)
776 if transport2
and not transport1
:
778 if transport1
and not transport2
:
784 if priority1
> priority2
:
786 if priority2
> priority1
:
788 server1
= common
.gajim
.get_server_from_jid(jid1
)
789 server2
= common
.gajim
.get_server_from_jid(jid2
)
790 myserver1
= common
.gajim
.config
.get_per('accounts', account1
, 'hostname')
791 myserver2
= common
.gajim
.config
.get_per('accounts', account2
, 'hostname')
792 if server1
== myserver1
:
793 if server2
!= myserver2
:
795 elif server2
== myserver2
:
801 # If all is the same, compare accounts, they can't be the same
802 if account1
> account2
:
804 if account2
> account1
:
808 def get_nearby_family_and_big_brother(self
, family
, account
):
810 Return the nearby family and its Big Brother
812 Nearby family is the part of the family that is grouped with the
813 metacontact. A metacontact may be over different accounts. If accounts
814 are not merged then the given family is split account wise.
816 (nearby_family, big_brother_jid, big_brother_account)
818 if common
.gajim
.config
.get('mergeaccounts'):
820 nearby_family
= family
822 # we want one nearby_family per account
823 nearby_family
= [data
for data
in family
if account
== data
['account']]
825 big_brother_data
= self
._get
_metacontacts
_big
_brother
(nearby_family
)
826 big_brother_jid
= big_brother_data
['jid']
827 big_brother_account
= big_brother_data
['account']
829 return (nearby_family
, big_brother_jid
, big_brother_account
)
831 def _get_metacontacts_big_brother(self
, family
):
833 Which of the family will be the big brother under wich all others will be
836 family
.sort(cmp=self
._compare
_metacontacts
)