Switching texting over to callbacks and changing the google voice work around to...
authorEd Page <eopage@byu.net>
Fri, 26 Mar 2010 01:44:03 +0000 (20:44 -0500)
committerEd Page <eopage@byu.net>
Fri, 26 Mar 2010 01:44:03 +0000 (20:44 -0500)
src/channel/text.py
src/gvoice/conversations.py

index bd54a81..1f15dbf 100644 (file)
@@ -20,7 +20,6 @@ class TextChannel(tp.ChannelTypeText):
 
                tp.ChannelTypeText.__init__(self, connection, manager, props)
                self.__nextRecievedId = 0
-               self.__hasServerBeenPolled = False
 
                self.__otherHandle = contactHandle
 
@@ -58,26 +57,28 @@ class TextChannel(tp.ChannelTypeText):
                if messageType != telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
                        raise telepathy.errors.NotImplemented("Unhandled message type: %r" % messageType)
 
-               if not self.__hasServerBeenPolled:
-                       # Hack: GV marks messages as read when they are replied to.  If GV
-                       # marks them as read than we ignore them.  So reduce the window for
-                       # them being marked as read.  Oh and Conversations already handles
-                       # it if the message was already part of a thread, so we can limit
-                       # this to if we are trying to start a thread.  You might say a
-                       # voicemail could be what is being replied to and that doesn't mean
-                       # anything.  Oh well.
-                       try:
-                               self._conn.session.texts.update(force=True)
-                       except Exception:
-                               _moduleLogger.exception(
-                                       "Update failed when proactively checking for texts"
-                               )
-
                _moduleLogger.info("Sending message to %r" % (self.__otherHandle, ))
-               self._conn.session.backend.send_sms([self.__otherHandle.phoneNumber], text)
-               self._conn.session.textsStateMachine.reset_timers()
+               self._conn.session.pool.add_task(
+                       self._conn.session.backend.send_sms,
+                       ([self.__otherHandle.phoneNumber], text),
+                       {},
+                       self._on_send_sms(messageType, text),
+                       self._on_send_sms_failed,
+               )
+
+       def _on_send_sms(self, messageType, text):
+
+               @misc_utils.log_exception(_moduleLogger)
+               def _actual_on_send_sms(self, *args):
+                       self._conn.session.textsStateMachine.reset_timers()
+
+                       self.Sent(int(time.time()), messageType, text)
 
-               self.Sent(int(time.time()), messageType, text)
+               return _actual_on_send_sms
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_send_sms_failed(self, error):
+               _moduleLogger.error(error)
 
        @misc_utils.log_exception(_moduleLogger)
        def Close(self):
@@ -120,9 +121,9 @@ class TextChannel(tp.ChannelTypeText):
                # Can't filter out messages in a texting conversation that came in
                # before the last one sent because that creates a race condition of two
                # people sending at about the same time, which happens quite a bit
+               newConversations = gvoice.conversations.filter_out_self(newConversations)
                newConversations = self._filter_out_reported(newConversations)
                newConversations = gvoice.conversations.filter_out_read(newConversations)
-               newConversations = gvoice.conversations.filter_out_self(newConversations)
                newConversations = list(newConversations)
                if not newConversations:
                        _moduleLogger.debug(
@@ -147,7 +148,6 @@ class TextChannel(tp.ChannelTypeText):
 
                for conv in newConversations:
                        conv.isRead = True
-               self.__hasServerBeenPolled = True
 
        def _format_message(self, message):
                return " ".join(part.text.strip() for part in message.body)
index 2aff337..86697ea 100644 (file)
@@ -28,6 +28,8 @@ class Conversations(object):
                self._get_raw_conversations = getter
                self._asyncPool = asyncPool
                self._conversations = {}
+               self._loadedFromCache = False
+               self._hasDoneUpdate = False
 
                self.updateSignalHandler = coroutines.CoTee()
 
@@ -50,6 +52,7 @@ class Conversations(object):
                ) <= 0:
                        _moduleLogger.info("%s Loaded cache" % (self._name, ))
                        self._conversations = convs
+                       self._loadedFromCache = True
                else:
                        _moduleLogger.debug(
                                "%s Skipping cache due to version mismatch (%s-%s)" % (
@@ -94,8 +97,12 @@ class Conversations(object):
                                mergedConversations = MergedConversations()
                                self._conversations[key] = mergedConversations
 
+                       if self._loadedFromCache or self._hasDoneUpdate:
+                               markAllAsRead = False
+                       else:
+                               markAllAsRead = True
                        try:
-                               mergedConversations.append_conversation(conversation)
+                               mergedConversations.append_conversation(conversation, markAllAsRead)
                                isConversationUpdated = True
                        except RuntimeError, e:
                                if False:
@@ -108,6 +115,7 @@ class Conversations(object):
                if updateConversationIds:
                        message = (self, updateConversationIds, )
                        self.updateSignalHandler.stage.send(message)
+               self._hasDoneUpdate = True
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_get_conversations_failed(self, error):
@@ -134,24 +142,31 @@ class MergedConversations(object):
        def __init__(self):
                self._conversations = []
 
-       def append_conversation(self, newConversation):
+       def append_conversation(self, newConversation, markAllAsRead):
                self._validate(newConversation)
-               similarExist = False
                for similarConversation in self._find_related_conversation(newConversation.id):
                        self._update_previous_related_conversation(similarConversation, newConversation)
                        self._remove_repeats(similarConversation, newConversation)
-                       similarExist = True
-               if similarExist:
-                       # Hack to reduce a race window with GV marking messages as read
-                       # because it thinks we replied when really we replied to the
-                       # previous message.  Clients of this code are expected to handle
-                       # this gracefully.  Other race conditions may exist but clients are
-                       # responsible for them
-                       if newConversation.messages:
-                               newConversation.isRead = False
-                       else:
-                               newConversation.isRead = True
-               self._conversations.append(newConversation)
+
+               # HACK: Because GV marks all messages as read when you reply it has
+               # the following race:
+               # 1. Get all messages
+               # 2. Contact sends a text
+               # 3. User sends a text marking contacts text as read
+               # 4. Get all messages not returning text from step 2
+               # This isn't a problem for voicemails but we don't know(?( enough.
+               # So we hack around this by:
+               # * We cache to disk the history of messages sent/received
+               # * On first run we mark all server messages as read due to no cache
+               # * If not first load or from cache (disk or in-memory) then it must be unread
+               if markAllAsRead:
+                       newConversation.isRead = True
+               else:
+                       newConversation.isRead = False
+
+               if newConversation.messages:
+                       # must not have had all items removed due to duplicates
+                       self._conversations.append(newConversation)
 
        def to_dict(self):
                selfDict = {}