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
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]
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}
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
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
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
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