e184d2d6ddb97092530d8697205138a86ae9c8dc
[theonering] / src / channel / contact_list.py
1 import logging
2
3 import telepathy
4
5 import tp
6 import util.coroutines as coroutines
7 import util.misc as misc_utils
8 import handle
9
10
11 _moduleLogger = logging.getLogger("channel.contact_list")
12
13
14 class AllContactsListChannel(
15                 tp.ChannelTypeContactList,
16                 tp.ChannelInterfaceGroup,
17         ):
18         """
19         The group of contacts for whom you receive presence
20         """
21
22         def __init__(self, connection, manager, props, listHandle):
23                 tp.ChannelTypeContactList.__init__(self, connection, manager, props)
24                 tp.ChannelInterfaceGroup.__init__(self)
25
26                 self.__manager = manager
27                 self.__props = props
28                 self.__session = connection.session
29                 self.__listHandle = listHandle
30                 self.__members = set()
31
32                 if self._conn.options.useGVContacts:
33                         self._callback = coroutines.func_sink(
34                                 coroutines.expand_positional(
35                                         self._on_contacts_refreshed
36                                 )
37                         )
38                         self.__session.addressbook.updateSignalHandler.register_sink(
39                                 self._callback
40                         )
41
42                         addressbook = connection.session.addressbook
43                         contacts = addressbook.get_numbers()
44                         self._process_refresh(addressbook, set(contacts), set(), set())
45                 else:
46                         self._callback = None
47
48                 self.GroupFlagsChanged(0, 0)
49
50
51         @misc_utils.log_exception(_moduleLogger)
52         def Close(self):
53                 self.close()
54
55         def close(self):
56                 _moduleLogger.debug("Closing contact list")
57                 if self._callback is not None:
58                         self.__session.addressbook.updateSignalHandler.unregister_sink(
59                                 self._callback
60                         )
61                         self._callback = None
62
63                 tp.ChannelTypeContactList.Close(self)
64                 self.remove_from_connection()
65
66         @misc_utils.log_exception(_moduleLogger)
67         def GetLocalPendingMembersWithInfo(self):
68                 return []
69
70         @misc_utils.log_exception(_moduleLogger)
71         def _on_contacts_refreshed(self, addressbook, added, removed, changed):
72                 self._process_refresh(addressbook, added, removed, changed)
73
74         def _is_on_list(self, number):
75                 return True
76
77         def _is_on_list_changed(self, number):
78                 return (number in self.__members) ^ (self._is_on_list(number))
79
80         def _is_now_on_list(self, number):
81                 return number not in self.__members and self._is_on_list(number)
82
83         def _was_on_list(self, number):
84                 return number in self.__members and not self._is_on_list(number)
85
86         def _process_refresh(self, addressbook, added, removed, changed):
87                 _moduleLogger.info(
88                         "%s Added: %r, Removed: %r" % (self.__listHandle.get_name(), len(added), len(removed))
89                 )
90                 connection = self._conn
91
92                 # convert changed into added/removed
93                 alsoAdded = set(
94                         number
95                         for number in changed
96                         if self._is_now_on_list(number)
97                 )
98                 alsoRemoved = set(
99                         number
100                         for number in changed
101                         if self._was_on_list(number)
102                 )
103
104                 # Merge the added/removed with changed
105                 added = [
106                         contactNumber
107                         for contactNumber in added
108                         if self._is_on_list(contactNumber)
109                 ]
110                 added.extend(alsoAdded)
111                 added.sort()
112                 handlesAdded = [
113                         handle.create_handle(connection, "contact", contactNumber)
114                         for contactNumber in added
115                 ]
116                 self.__members.union(added)
117
118                 removed = list(removed)
119                 removed.extend(alsoRemoved)
120                 handlesRemoved = [
121                         handle.create_handle(connection, "contact", contactNumber)
122                         for contactNumber in removed
123                 ]
124                 self.__members.difference(removed)
125
126                 message = ""
127                 actor = 0
128                 reason = telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE
129                 self.MembersChanged(
130                         message,
131                         handlesAdded, handlesRemoved,
132                         (), (),
133                         actor,
134                         reason,
135                 )
136
137
138 class DenyContactsListChannel(AllContactsListChannel):
139
140         def _is_on_list(self, number):
141                 return self._conn.session.addressbook.is_blocked(number)
142
143
144 _LIST_TO_FACTORY = {
145         # The group of contacts for whom you receive presence
146         'subscribe': AllContactsListChannel,
147         # The group of contacts who may receive your presence
148         'publish': AllContactsListChannel,
149         # A group of contacts who are on the publish list but are temporarily
150         # disallowed from receiving your presence
151         # This doesn't make sense to support
152         'hide': None,
153         # A group of contacts who may send you messages
154         # Is this meant to serve as a definitive white list for all contact?
155         'allow': None,
156         # A group of contacts who may not send you messages
157         'deny': DenyContactsListChannel,
158         # On protocols where the user's contacts are stored, this contact list
159         # contains all stored contacts regardless of subscription status.
160         'stored': AllContactsListChannel,
161 }
162
163
164 _SUPPORTED_LISTS = frozenset(
165         name
166         for name in _LIST_TO_FACTORY.iterkeys()
167         if name is not None
168 )
169
170
171 def create_contact_list_channel(connection, manager, props, h):
172         factory = _LIST_TO_FACTORY.get(h.get_name(), None)
173         if factory is None:
174                 raise telepathy.errors.NotCapable("Unsuported type %s" % h.get_name())
175         return factory(connection, manager, props, h)
176
177
178 def get_spported_lists():
179         return _SUPPORTED_LISTS