Beispiel #1
0
def find_or_create_discussion(user, params):
    """Find a related discussion or create a new one."""
    addresses = [x['address'] for x in params.get('participants', [])]
    addresses = list(set(addresses))
    addresses.sort()
    hashed = hashlib.sha256(''.join(addresses)).hexdigest()
    try:
        lookup = DiscussionGlobalLookup.get(user, hashed)
        log.info('Found existing discussion {0}'.format(lookup.discussion_id))
        return lookup.discussion_id
    except NotFound:
        # Create a new discussion
        discussion_id = uuid.uuid4()
        log.info('Creating new discussion {0}'.format(discussion_id))
        Discussion.create(user=user, discussion_id=discussion_id)
        DiscussionGlobalLookup.create(user=user,
                                      hashed=hashed,
                                      discussion_id=discussion_id)
        return discussion_id
Beispiel #2
0
    def lookup_discussion_sequence(self, mail, message):
        """
        Return list of lookups (type, value) from a mail message
        and the first lookup'hash from that list
        """
        seq = []

        # lists lookup first
        lists = []
        for list_id in mail.extra_parameters.get('lists', []):
            lists.append(list_id)
        if len(lists) > 0:
            # list_ids are considered `participants`
            # build uris' hash and upsert lookup tables
            participants = []
            for list_id in lists:
                participant = Participant()
                participant.address = list_id.lower()
                participant.protocol = 'email'
                participant.type = 'list-id'
                participants.append(participant)
                discuss = Discussion(self.user)
                discuss.upsert_lookups_for_participants(participants)
                # add list-id as a participant to the message
                message.participants.append(participant)
            hash = hash_participants_uri(participants)
            seq.append(('list', hash['hash']))

        # then participants
        seq.append(('hash', message.hash_participants))

        # try to link message to external thread's root message-id
        #        if len(message.external_references["ancestors_ids"]) > 0:
        #            seq.append(("thread",
        #                        message.external_references["ancestors_ids"][0]))
        #        elif message.external_references["parent_id"]:
        #            seq.append(("thread", message.external_references["parent_id"]))
        #        elif message.external_references["message_id"]:
        #            seq.append(("thread", message.external_references["message_id"]))

        return seq, seq[0][1]
Beispiel #3
0
 def collection_post(self):
     participants = self.request.swagger_data['participants']
     msg = NewMessage()
     parts = []
     for part in participants:
         participant = Participant()
         participant.address = part['address']
         participant.label = part['label']
         participant.protocol = part['protocol']
         participant.contact_id = part.get('contact_ids', [])
         parts.append(participant)
     msg.participants = parts
     hashed = msg.hash_participants
     discussion = Discussion.by_hash(self.user, hashed)
     did = discussion.discussion_id if discussion else ""
     return {'hash': hashed, 'discussion_id': did}
Beispiel #4
0
 def collection_post(self):
     participants = self.request.swagger_data['participants']
     msg = NewMessage()
     parts = []
     for part in participants:
         participant = Participant()
         participant.address = part['address']
         participant.label = part['label']
         participant.protocol = part['protocol']
         participant.contact_id = part.get('contact_ids', [])
         parts.append(participant)
     msg.participants = parts
     hashed = msg.hash_participants
     discussion = Discussion.by_hash(self.user, hashed)
     did = discussion.discussion_id if discussion else ""
     return {'hash': hashed,
             'discussion_id': did}
Beispiel #5
0
    def process_inbound(self, raw):
        """Process inbound message.

        @param raw: a RawMessage object
        @rtype: NewMessage
        """
        message = MailMessage(raw.raw_data)
        new_message = NewInboundMessage()
        new_message.raw_msg_id = raw.raw_msg_id
        new_message.subject = message.subject
        new_message.body_html = message.body_html
        new_message.body_plain = message.body_plain
        new_message.date = message.date
        new_message.size = message.size
        new_message.protocol = message.message_protocol
        new_message.is_unread = True
        new_message.is_draft = False
        new_message.is_answered = False
        new_message.is_received = True
        new_message.importance_level = 0  # XXX tofix on parser
        new_message.external_references = message.external_references

        participants = []
        for p in message.participants:
            participant, contact = self.get_participant(message, p)
            new_message.participants.append(participant)
            participants.append((participant, contact))

        if not participants:
            raise Exception("no participant found in raw message {}".format(
                raw.raw_msg_id))

        for a in message.attachments:
            attachment = Attachment()
            attachment.content_type = a.content_type
            attachment.file_name = a.filename
            attachment.size = a.size
            attachment.mime_boundary = a.mime_boundary
            if hasattr(a, "is_inline"):
                attachment.is_inline = a.is_inline
            new_message.attachments.append(attachment)

        # Compute PI !!
        conf = Configuration('global').configuration
        extractor = InboundMailFeature(message, conf)
        extractor.process(self.user, new_message, participants)

        # compute tags
        self._get_tags(new_message)
        if new_message.tags:
            log.debug('Resolved tags {}'.format(new_message.tags))

        # lookup by external references
        lookup_sequence = self.lookup_discussion_sequence(message, new_message)
        lkp = self.lookup(lookup_sequence)
        log.debug('Lookup with sequence {} give {}'.
                  format(lookup_sequence, lkp))

        if lkp:
            new_message.discussion_id = lkp.discussion_id
        else:
            discussion = Discussion.create_from_message(self.user, message)
            log.debug('Created discussion {}'.format(discussion.discussion_id))
            new_message.discussion_id = discussion.discussion_id
            self.create_lookups(lookup_sequence, new_message)
        # Format features
        new_message.privacy_features = \
            marshal_features(new_message.privacy_features)
        try:
            new_message.validate()
        except Exception as exc:
            log.error(
                "validation failed with error : « {} » \
                for new_message {}[dump : {}]".format(
                    exc, new_message, vars(new_message)))
            raise exc

        return new_message
Beispiel #6
0
    def process_inbound(self, raw):
        """
        Process inbound message.

        @param raw: a RawMessage object
            which should be a json conforming to
            https://developer.twitter.com/en/docs/direct-messages/\
            sending-and-receiving/guides/message-create-object
        @rtype: NewMessage
        """
        message = TwitterDM(raw.raw_data)
        new_message = NewInboundMessage()
        new_message.raw_msg_id = raw.raw_msg_id
        new_message.body_plain = message.body_plain
        new_message.date = message.date
        new_message.protocol = message.protocol
        new_message.is_unread = True
        new_message.is_draft = False
        new_message.is_answered = False
        new_message.is_received = True
        new_message.importance_level = 0  # XXX tofix on parser
        new_message.external_references = message.external_references

        participants = []
        for p in message.participants:
            participant, contact = self.get_participant(message, p)
            new_message.participants.append(participant)
            participants.append(participant)
        if not participants:
            raise Exception("no participant found in raw message {}".format(
                raw.raw_msg_id))

        # Compute PI !!
        # TODO

        # compute tags
        self._get_tags(new_message)
        if new_message.tags:
            log.debug('Resolved tags {}'.format(new_message.tags))

        # lookup by external references
        lookup_sequence = self.lookup_discussion_sequence(new_message)
        lkp = self.lookup(lookup_sequence)
        log.debug('Lookup with sequence {} give {}'.format(
            lookup_sequence, lkp))

        if lkp:
            new_message.discussion_id = lkp.discussion_id
        else:
            discussion = Discussion.create_from_message(self.user, message)
            log.debug('Created discussion {}'.format(discussion.discussion_id))
            new_message.discussion_id = discussion.discussion_id
            self.create_lookups(lookup_sequence, new_message)
        # Format features
        new_message.privacy_features = \
            marshal_features(new_message.privacy_features)
        try:
            new_message.validate()
        except Exception as exc:
            log.error("validation failed with error : « {} » \
                for new_message {}[dump : {}]".format(exc, new_message,
                                                      vars(new_message)))
            raise exc

        return new_message
    def process_inbound(self, raw):
        """
        Process inbound message.

        @param raw: a RawMessage object
            which should be a json conforming to
            https://docs.joinmastodon.org/api/entities/#status
        @rtype: NewMessage
        """
        toot = MastodonStatus(raw.raw_data)
        new_message = NewInboundMessage()
        new_message.raw_msg_id = raw.raw_msg_id
        new_message.body_html = toot.body_html
        new_message.date = toot.date
        new_message.protocol = toot.protocol
        new_message.is_unread = True
        new_message.is_draft = False
        new_message.is_answered = False
        new_message.is_received = True
        new_message.importance_level = 0  # XXX tofix on parser
        new_message.external_references = toot.external_references

        participants = []
        for p in toot.participants:
            p.address = p.address
            participant, contact = self.get_participant(toot, p)
            new_message.participants.append(participant)
            participants.append((participant, contact))

        if not participants:
            raise Exception("no participant found in raw tweet {}".format(
                raw.raw_msg_id))

        # Compute PI !!
        # TODO

        # compute tags
        self._get_tags(new_message)
        if new_message.tags:
            log.debug('Resolved tags {}'.format(new_message.tags))

        # build discussion_id from lookup_sequence
        lookup_sequence, discussion_id = self.lookup_discussion_sequence(
            new_message)
        log.debug('Lookup with sequence {} gives {}'.format(
            lookup_sequence, discussion_id))
        new_message.discussion_id = discussion_id

        # upsert lookup tables
        discuss = Discussion(self.user)
        discuss.upsert_lookups_for_participants(new_message.participants)
        # Format features
        new_message.privacy_features = \
            marshal_features(new_message.privacy_features)
        try:
            new_message.validate()
        except Exception as exc:
            log.error("validation failed with error : « {} » \
                for new_message {}[dump : {}]".format(exc, new_message,
                                                      vars(new_message)))
            raise exc

        return new_message
Beispiel #8
0
    def _check_discussion_consistency(self, user):
        from caliopen_main.message.objects.message import Message
        new_discussion = False
        if not hasattr(self, 'discussion_id') or self.discussion_id == "" \
                or self.discussion_id is None:
            # no discussion_id provided. Try to find one with draft's parent_id
            # or create new discussion
            if hasattr(self, 'parent_id') \
                    and self.parent_id is not None \
                    and self.parent_id != "":
                parent_msg = Message(user, message_id=self.parent_id)
                try:
                    parent_msg.get_db()
                    parent_msg.unmarshall_db()
                except NotFound:
                    raise err.PatchError(message="parent message not found")
                self.discussion_id = parent_msg.discussion_id
            else:
                discussion = Discussion.create_from_message(user, self)
                self.discussion_id = discussion.discussion_id
                new_discussion = True
        if not new_discussion:
            dim = DIM(user)
            d_id = self.discussion_id
            last_message = dim.get_last_message(d_id, -10, 10, True)
            if last_message == {}:
                raise err.PatchError(
                    message='No such discussion {}'.format(d_id))
            is_a_reply = (str(last_message.message_id) != str(self.message_id))
            if is_a_reply:
                # check participants consistency
                if hasattr(self,
                           "participants") and len(self.participants) > 0:
                    participants = [p['address'] for p in self.participants]
                    last_msg_participants = [
                        p['address'] for p in last_message.participants
                    ]
                    if len(participants) != len(last_msg_participants):
                        raise err.PatchError(
                            message="list of participants "
                            "is not consistent for this discussion")
                    participants.sort()
                    last_msg_participants.sort()

                    for i, participant in enumerate(participants):
                        if participant != last_msg_participants[i]:
                            raise err.PatchConflict(
                                message="list of participants "
                                "is not consistent for this discussion")
                else:
                    self.build_participants_for_reply(user)

                # check parent_id consistency
                if 'parent_id' in self and self.parent_id != "" \
                        and self.parent_id is not None:
                    if not dim.message_belongs_to(
                            discussion_id=self.discussion_id,
                            message_id=self.parent_id):
                        raise err.PatchConflict(message="provided message "
                                                "parent_id does not belong "
                                                "to this discussion")
                else:
                    self.parent_id = last_message.parent_id

                self.update_external_references(user)

            else:
                last_message = None
        else:
            last_message = None

        return last_message
Beispiel #9
0
    def process_inbound(self, raw):
        """Process inbound message.

        @param raw: a RawMessage object
        @rtype: NewMessage
        """
        email = MailMessage(raw.raw_data)
        new_message = NewInboundMessage()
        new_message.raw_msg_id = raw.raw_msg_id
        new_message.subject = email.subject
        new_message.body_html = email.body_html
        new_message.body_plain = email.body_plain
        new_message.date = email.date
        new_message.size = email.size
        new_message.protocol = email.message_protocol
        new_message.is_unread = True
        new_message.is_draft = False
        new_message.is_answered = False
        new_message.is_received = True
        new_message.importance_level = 0  # XXX tofix on parser
        new_message.external_references = email.external_references

        participants = []
        for p in email.participants:
            p.address = p.address.lower()
            try:
                participant, contact = self.get_participant(email, p)
                new_message.participants.append(participant)
                participants.append((participant, contact))
            except Exception as exc:
                log.error(
                    "process_inbound failed to lookup participant for email {} : {}"
                    .format(vars(email), exc))
                raise exc

        if not participants:
            raise Exception("no participant found in raw email {}".format(
                raw.raw_msg_id))

        for a in email.attachments:
            attachment = Attachment()
            attachment.content_type = a.content_type
            attachment.file_name = a.filename
            attachment.size = a.size
            attachment.mime_boundary = a.mime_boundary
            if hasattr(a, "is_inline"):
                attachment.is_inline = a.is_inline
            new_message.attachments.append(attachment)

        # Compute PI !!
        conf = Configuration('global').configuration
        extractor = InboundMailFeature(email, conf)
        extractor.process(self.user, new_message, participants)

        # compute user tags
        self._get_tags(new_message)
        # embed external flags if any
        new_message.ext_tags = email.external_flags
        if new_message.tags:
            log.debug('Resolved tags {}'.format(new_message.tags))

        # build discussion_id from lookup_sequence
        lookup_sequence, discussion_id = self.lookup_discussion_sequence(
            email, new_message)
        log.debug('Lookup with sequence {} gives {}'.format(
            lookup_sequence, discussion_id))
        new_message.discussion_id = discussion_id

        # upsert lookup tables
        discuss = Discussion(self.user)
        discuss.upsert_lookups_for_participants(new_message.participants)
        # Format features
        new_message.privacy_features = \
            marshal_features(new_message.privacy_features)
        try:
            new_message.validate()
        except Exception as exc:
            log.error("validation failed with error : « {} » \
                for new_message {}[dump : {}]".format(exc, new_message,
                                                      vars(new_message)))
            raise exc

        return new_message