def testOutbox(self): '''Test the storage and retrieval of messages in the outbox''' db = supersimpledb.MurmeliDb() DbI.setDb(db) self.assertEqual(len(DbI.getOutboxMessages()), 0, "Outbox should be empty") # Add profile for this the target recipient DbI.updateProfile("ABC312", {"keyid":"ZYXW987", "status":"trusted", "name":"Best friend"}) # add one message to the outbox DbI.addToOutbox(ExampleMessage(["ABC312"], "Doesn't matter really what the message is")) self.assertEqual(len(DbI.getOutboxMessages()), 1, "Outbox should have one message") self.checkMessageIndexes(DbI.getOutboxMessages()) DbI.addToOutbox(ExampleMessage(["ABC312"], "A second message")) self.assertEqual(len(DbI.getOutboxMessages()), 2, "Outbox should have 2 messages") self.checkMessageIndexes(DbI.getOutboxMessages()) self.assertTrue(DbI.deleteFromOutbox(0)) self.assertEqual(len(DbI.getOutboxMessages()), 1, "Outbox should only have 1 message (1 empty)") nonEmptyMessages = DbI.getOutboxMessages() self.assertEqual(len(nonEmptyMessages), 1, "Outbox should only have 1 non-empty message") self.assertEqual(nonEmptyMessages[0]["_id"], 1, "Message 0 should have index 1") # See if index of third message is properly assigned DbI.addToOutbox(ExampleMessage(["ABC312"], "A third message")) self.assertEqual(len(DbI.getOutboxMessages()), 2, "Outbox should have 2 messages again") self.assertEqual(DbI.getOutboxMessages()[0]["_id"], 1, "Message 0 should have index 1") self.assertEqual(DbI.getOutboxMessages()[1]["_id"], 2, "Message 1 should have index 2") # done DbI.releaseDb()
def flushOutboxInSeparateThread(self): '''This can take quite a while to do the flush''' if self._flushing: return print("Outgoing postman is flushing the outbox...") self._flushing = True # Look in the outbox for messages messagesFound = 0 messagesSent = 0 failedRecpts = set() for m in DbI.getOutboxMessages(): if not m: continue # message already deleted messagesFound += 1 # Get recipient, timestamp, relays, message message = imageutils.stringToBytes(m['message']) sendTimestamp = m.get('timestamp', None) # not used yet # TODO: if the timestamp is too old, then either just delete the message (if it's not to be queued) or move to inbox # Some messages have a single recipient (and maybe relays), others only have a recipientList recipient = m.get('recipient', None) if recipient: sendSuccess = self.RC_MESSAGE_FAILED if recipient in failedRecpts else self.sendMessage( message, recipient) if sendSuccess == self.RC_MESSAGE_IGNORED: print( "Dealt with message so I should delete it from the db:", m["_id"]) DbI.deleteFromOutbox(m["_id"]) elif sendSuccess == self.RC_MESSAGE_SENT: print("Sent message so I should delete it from the db:", m["_id"]) DbI.deleteFromOutbox(m["_id"]) messagesSent += 1 testMsg = "Message sent, type was %s and recipient was %s" % ( m.get("msgType", "unknown"), recipient) # TODO: Pass these values with the signal as an object, not a string self.messageSentSignal.emit(testMsg) elif not m.get('queue', False): print( "I failed to send a message but it shouldn't be queued, deleting it" ) DbI.deleteFromOutbox(m["_id"]) failedRecpts.add(recipient) else: print( "I failed to send but I'll keep the message and try again later" ) failedRecpts.add(recipient) # Message couldn't be sent directly, but maybe there are some relays we can use relays = m.get('relays', None) sentSomething = False if relays: # TODO: Check current timestamp and compare with sendTimestamp (types?) failedrelays = set() signedRelayMessage = m.get('relayMessage', None) if not signedRelayMessage: # Take the message bytes and make a RelayingMessage out of them to get the signed output signedRelayMessage = RelayingMessage( message).createOutput(None) # TODO: Store signedRelayMessage back in m # Loop over each relay in the list and try to send to each one for relay in relays: # Should we try to send if this relay is not online? On the other hand it doesn't hurt. if relay not in failedRecpts and self.sendMessage( signedRelayMessage, relay) == self.RC_MESSAGE_SENT: print("Sent message to relay '%s'" % relay) sentSomething = True messagesSent += 1 else: # Send failed, so add this relay to the list of failed ones failedrelays.add(relay) if sentSomething: DbI.updateOutboxMessage( m["_id"], {"relays": list(failedrelays)}) else: # There isn't a direct recipient, so let's hope there's a recipient list recipientList = m.get('recipientList', None) if recipientList: print("I've got a message to relay to: ", recipientList) failedRecipientsForThisMessage = set() # Try to send to each in the list for rRecipient in recipientList: if rRecipient in failedRecpts or self.sendMessage( message, rRecipient) == self.RC_MESSAGE_FAILED: # Couldn't send to this recipient failedRecipientsForThisMessage.add(rRecipient) if failedRecipientsForThisMessage: # Update m with the recipientList = failedRecipientsForThisMessage DbI.updateOutboxMessage(m["_id"], { "recipientList": list(failedRecipientsForThisMessage) }) print("I failed to send a relay to:", failedRecipientsForThisMessage) else: print( "I managed to relay everything, now deleting relay message" ) DbI.deleteFromOutbox(m["_id"]) # TODO: Wait inbetween sending to avoid overloading the network # TODO: Does the parent even need to know when a send has worked? if messagesSent > 0: self.parent.postmanKnock() # only once if messagesFound > 0: print("For %d found messages, I managed to send %d copies" % (messagesFound, messagesSent)) # We tried to send a message to these recipients but failed - set them to be offline for r in failedRecpts: Contacts.instance().goneOffline(r) self._flushing = False