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