def by_pm_with(self, query: Query, operand: Union[str, Iterable[int]], maybe_negate: ConditionTransform) -> Query: try: if isinstance(operand, str): email_list = operand.split(",") user_profiles = get_user_profiles( emails=email_list, realm=self.user_realm, ) else: """ This is where we handle passing a list of user IDs for the narrow, which is the preferred/cleaner API. """ user_profiles = get_user_profiles_by_ids( user_ids=operand, realm=self.user_realm, ) recipient = recipient_for_user_profiles(user_profiles=user_profiles, forwarded_mirror_message=False, forwarder_user_profile=None, sender=self.user_profile, allow_deactivated=True) except (JsonableError, ValidationError): raise BadNarrowOperator('unknown user in ' + str(operand)) # Group DM if recipient.type == Recipient.HUDDLE: cond = column("recipient_id") == recipient.id return query.where(maybe_negate(cond)) # 1:1 PM other_participant = None # Find if another person is in PM for user in user_profiles: if user.id != self.user_profile.id: other_participant = user # PM with another person if other_participant: # We need bidirectional messages PM with another person. # But Recipient.PERSONAL objects only encode the person who # received the message, and not the other participant in # the thread (the sender), we need to do a somewhat # complex query to get messages between these two users # with either of them as the sender. self_recipient_id = self.user_profile.recipient_id cond = or_(and_(column("sender_id") == other_participant.id, column("recipient_id") == self_recipient_id), and_(column("sender_id") == self.user_profile.id, column("recipient_id") == recipient.id)) return query.where(maybe_negate(cond)) # PM with self cond = and_(column("sender_id") == self.user_profile.id, column("recipient_id") == recipient.id) return query.where(maybe_negate(cond))
def further_validated_draft_dict(draft_dict: Dict[str, Any], user_profile: UserProfile) -> Dict[str, Any]: """ Take a draft_dict that was already validated by draft_dict_validator then further sanitize, validate, and transform it. Ultimately return this "further validated" draft dict. It will have a slightly different set of keys the values for which can be used to directly create a Draft object. """ content = truncate_body(draft_dict["content"]) if "\x00" in content: raise JsonableError(_("Content must not contain null bytes")) timestamp = draft_dict.get("timestamp", time.time()) timestamp = round(timestamp, 6) if timestamp < 0: # While it's not exactly an invalid timestamp, it's not something # we want to allow either. raise JsonableError(_("Timestamp must not be negative.")) last_edit_time = timestamp_to_datetime(timestamp) topic = "" recipient = None to = draft_dict["to"] if draft_dict["type"] == "stream": topic = truncate_topic(draft_dict["topic"]) if "\x00" in topic: raise JsonableError(_("Topic must not contain null bytes")) if len(to) != 1: raise JsonableError( _("Must specify exactly 1 stream ID for stream messages")) stream, sub = access_stream_by_id(user_profile, to[0]) recipient = stream.recipient elif draft_dict["type"] == "private" and len(to) != 0: to_users = get_user_profiles_by_ids(set(to), user_profile.realm) try: recipient = recipient_for_user_profiles(to_users, False, None, user_profile) except ValidationError as e: # nocoverage raise JsonableError(e.messages[0]) return { "recipient": recipient, "topic": topic, "content": content, "last_edit_time": last_edit_time, }