Taking the same python-telepathy caution elsewhere
[theonering] / src / channel / text.py
1 import time
2 import datetime
3 import logging
4
5 import telepathy
6
7 import util.go_utils as gobject_utils
8 import util.coroutines as coroutines
9 import gtk_toolbox
10
11
12 _moduleLogger = logging.getLogger("channel.text")
13
14
15 class TextChannel(telepathy.server.ChannelTypeText):
16         """
17         Look into implementing ChannelInterfaceMessages for rich text formatting
18         """
19
20         def __init__(self, connection, manager, props, contactHandle):
21                 self.__manager = manager
22                 self.__props = props
23
24                 try:
25                         # HACK Older python-telepathy way
26                         telepathy.server.ChannelTypeText.__init__(self, connection, contactHandle)
27                 except TypeError:
28                         # HACK Newer python-telepathy way
29                         telepathy.server.ChannelTypeText.__init__(self, connection, manager, props)
30                 self.__nextRecievedId = 0
31                 self.__lastMessageTimestamp = datetime.datetime(1, 1, 1)
32
33                 self.__otherHandle = contactHandle
34
35                 self.__callback = coroutines.func_sink(
36                         coroutines.expand_positional(
37                                 self._on_conversations_updated
38                         )
39                 )
40                 self._conn.session.voicemails.updateSignalHandler.register_sink(
41                         self.__callback
42                 )
43                 self._conn.session.texts.updateSignalHandler.register_sink(
44                         self.__callback
45                 )
46
47                 # The only reason there should be anything in the conversation is if
48                 # its new, so report it all
49                 try:
50                         mergedConversations = self._conn.session.voicemails.get_conversation(self._contactKey)
51                 except KeyError:
52                         _moduleLogger.debug("Nothing in the conversation yet for %r" % (self._contactKey, ))
53                 else:
54                         self._report_conversation(mergedConversations)
55                 try:
56                         mergedConversations = self._conn.session.texts.get_conversation(self._contactKey)
57                 except KeyError:
58                         _moduleLogger.debug("Nothing in the conversation yet for %r" % (self._contactKey, ))
59                 else:
60                         self._report_conversation(mergedConversations)
61
62         @gtk_toolbox.log_exception(_moduleLogger)
63         def Send(self, messageType, text):
64                 if messageType != telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
65                         raise telepathy.errors.NotImplemented("Unhandled message type: %r" % messageType)
66
67                 self._conn.session.backend.send_sms(self.__otherHandle.phoneNumber, text)
68                 self._conn.session.textsStateMachine.reset_timers()
69
70                 self.Sent(int(time.time()), messageType, text)
71
72         @gtk_toolbox.log_exception(_moduleLogger)
73         def Close(self):
74                 self.close()
75
76         def close(self):
77                 self._conn.session.voicemails.updateSignalHandler.unregister_sink(
78                         self.__callback
79                 )
80                 self._conn.session.texts.updateSignalHandler.unregister_sink(
81                         self.__callback
82                 )
83                 self.__callback = None
84
85                 telepathy.server.ChannelTypeText.Close(self)
86                 if self.__manager.channel_exists(self.__props):
87                         # HACK Older python-telepathy requires doing this manually
88                         self.__manager.remove_channel(self)
89                 self.remove_from_connection()
90
91         @property
92         def _contactKey(self):
93                 contactKey = self.__otherHandle.contactID, self.__otherHandle.phoneNumber
94                 return contactKey
95
96         @gtk_toolbox.log_exception(_moduleLogger)
97         def _on_conversations_updated(self, conv, conversationIds):
98                 if self._contactKey not in conversationIds:
99                         return
100                 _moduleLogger.debug("Incoming messages from %r for existing conversation" % (self._contactKey, ))
101                 mergedConversations = conv.get_conversation(self._contactKey)
102                 self._report_conversation(mergedConversations)
103
104         def _report_conversation(self, mergedConversations):
105                 newConversations = mergedConversations.conversations
106                 newConversations = self._filter_out_reported(newConversations)
107                 newConversations = self._filter_out_read(newConversations)
108                 newConversations = list(newConversations)
109                 if not newConversations:
110                         _moduleLogger.debug(
111                                 "New messages for %r have already been read externally" % (self._contactKey, )
112                         )
113                         return
114                 self.__lastMessageTimestamp = newConversations[-1].time
115
116                 messages = [
117                         newMessage
118                         for newConversation in newConversations
119                         for newMessage in newConversation.messages
120                         if newMessage.whoFrom != "Me:"
121                 ]
122                 if not newConversations:
123                         _moduleLogger.debug(
124                                 "All incoming messages were really outbound messages for %r" % (self._contactKey, )
125                         )
126                         return
127
128                 for newMessage in messages:
129                         formattedMessage = self._format_message(newMessage)
130                         self._report_new_message(formattedMessage)
131
132         def _filter_out_reported(self, conversations):
133                 return (
134                         conversation
135                         for conversation in conversations
136                         if self.__lastMessageTimestamp < conversation.time
137                 )
138
139         def _filter_out_read(self, conversations):
140                 return (
141                         conversation
142                         for conversation in conversations
143                         if not conversation.isRead and not conversation.isArchived
144                 )
145
146         def _format_message(self, message):
147                 return " ".join(part.text.strip() for part in message.body)
148
149         def _report_new_message(self, message):
150                 currentReceivedId = self.__nextRecievedId
151                 timestamp = int(time.time())
152                 type = telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
153
154                 _moduleLogger.info("Received message from User %r" % self.__otherHandle)
155                 self.Received(currentReceivedId, timestamp, self.__otherHandle, type, 0, message)
156
157                 self.__nextRecievedId += 1