2024-07-12 02:30:25 +02:00
|
|
|
from collections.abc import Iterable, Sequence
|
|
|
|
from typing import cast
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2021-04-16 00:57:30 +02:00
|
|
|
from django.utils.translation import gettext as _
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2017-08-18 05:01:22 +02:00
|
|
|
from zerver.lib.exceptions import JsonableError
|
2022-01-11 21:49:40 +01:00
|
|
|
from zerver.lib.string_validation import check_stream_topic
|
2023-12-15 01:16:00 +01:00
|
|
|
from zerver.models import Realm, Stream, UserProfile
|
|
|
|
from zerver.models.users import (
|
2018-08-16 20:54:49 +02:00
|
|
|
get_user_by_id_in_realm_including_cross_realm,
|
2020-06-11 00:54:34 +02:00
|
|
|
get_user_including_cross_realm,
|
2017-08-18 02:15:51 +02:00
|
|
|
)
|
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
def get_user_profiles(emails: Iterable[str], realm: Realm) -> list[UserProfile]:
|
|
|
|
user_profiles: list[UserProfile] = []
|
2017-08-18 02:15:51 +02:00
|
|
|
for email in emails:
|
|
|
|
try:
|
2017-09-25 21:51:47 +02:00
|
|
|
user_profile = get_user_including_cross_realm(email, realm)
|
2017-08-18 02:15:51 +02:00
|
|
|
except UserProfile.DoesNotExist:
|
2023-07-17 22:40:33 +02:00
|
|
|
raise JsonableError(_("Invalid email '{email}'").format(email=email))
|
2017-08-18 02:15:51 +02:00
|
|
|
user_profiles.append(user_profile)
|
|
|
|
return user_profiles
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
def get_user_profiles_by_ids(user_ids: Iterable[int], realm: Realm) -> list[UserProfile]:
|
|
|
|
user_profiles: list[UserProfile] = []
|
2018-08-16 20:54:49 +02:00
|
|
|
for user_id in user_ids:
|
|
|
|
try:
|
|
|
|
user_profile = get_user_by_id_in_realm_including_cross_realm(user_id, realm)
|
|
|
|
except UserProfile.DoesNotExist:
|
2023-07-17 22:40:33 +02:00
|
|
|
raise JsonableError(_("Invalid user ID {user_id}").format(user_id=user_id))
|
2018-08-16 20:54:49 +02:00
|
|
|
user_profiles.append(user_profile)
|
|
|
|
return user_profiles
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-05 11:37:41 +01:00
|
|
|
class Addressee:
|
2017-04-27 22:48:06 +02:00
|
|
|
# This is really just a holder for vars that tended to be passed
|
|
|
|
# around in a non-type-safe way before this class was introduced.
|
|
|
|
#
|
|
|
|
# It also avoids some nonsense where you have to think about whether
|
2023-06-19 16:42:11 +02:00
|
|
|
# topic should be None or '' for a direct message, or you have to
|
|
|
|
# make an array of one stream.
|
2017-04-27 22:48:06 +02:00
|
|
|
#
|
|
|
|
# Eventually we can use this to cache Stream and UserProfile objects
|
|
|
|
# in memory.
|
|
|
|
#
|
|
|
|
# This should be treated as an immutable class.
|
2021-02-12 08:19:30 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
msg_type: str,
|
2024-07-12 02:30:23 +02:00
|
|
|
user_profiles: Sequence[UserProfile] | None = None,
|
|
|
|
stream: Stream | None = None,
|
|
|
|
stream_name: str | None = None,
|
|
|
|
stream_id: int | None = None,
|
|
|
|
topic_name: str | None = None,
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
assert msg_type in ["stream", "private"]
|
2024-01-13 09:55:16 +01:00
|
|
|
if msg_type == "stream" and topic_name is None:
|
2019-03-20 22:53:26 +01:00
|
|
|
raise JsonableError(_("Missing topic"))
|
2017-04-27 22:48:06 +02:00
|
|
|
self._msg_type = msg_type
|
2017-08-18 05:01:22 +02:00
|
|
|
self._user_profiles = user_profiles
|
2019-02-07 02:05:34 +01:00
|
|
|
self._stream = stream
|
2017-04-27 22:48:06 +02:00
|
|
|
self._stream_name = stream_name
|
2019-01-26 03:39:02 +01:00
|
|
|
self._stream_id = stream_id
|
2024-01-13 09:55:16 +01:00
|
|
|
self._topic_name = topic_name
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def is_stream(self) -> bool:
|
2021-02-12 08:20:45 +01:00
|
|
|
return self._msg_type == "stream"
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def is_private(self) -> bool:
|
2021-02-12 08:20:45 +01:00
|
|
|
return self._msg_type == "private"
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2019-08-10 00:30:33 +02:00
|
|
|
def user_profiles(self) -> Sequence[UserProfile]:
|
2021-02-12 08:19:30 +01:00
|
|
|
assert self.is_private()
|
2019-08-10 00:30:33 +02:00
|
|
|
assert self._user_profiles is not None
|
|
|
|
return self._user_profiles
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2024-07-12 02:30:23 +02:00
|
|
|
def stream(self) -> Stream | None:
|
2021-02-12 08:19:30 +01:00
|
|
|
assert self.is_stream()
|
2019-02-07 02:05:34 +01:00
|
|
|
return self._stream
|
|
|
|
|
2024-07-12 02:30:23 +02:00
|
|
|
def stream_name(self) -> str | None:
|
2021-02-12 08:19:30 +01:00
|
|
|
assert self.is_stream()
|
2017-04-27 22:48:06 +02:00
|
|
|
return self._stream_name
|
|
|
|
|
2024-07-12 02:30:23 +02:00
|
|
|
def stream_id(self) -> int | None:
|
2021-02-12 08:19:30 +01:00
|
|
|
assert self.is_stream()
|
2019-01-26 03:39:02 +01:00
|
|
|
return self._stream_id
|
|
|
|
|
2024-01-13 09:55:16 +01:00
|
|
|
def topic_name(self) -> str:
|
2021-02-12 08:19:30 +01:00
|
|
|
assert self.is_stream()
|
2024-01-13 09:55:16 +01:00
|
|
|
assert self._topic_name is not None
|
|
|
|
return self._topic_name
|
2017-04-27 22:48:06 +02:00
|
|
|
|
|
|
|
@staticmethod
|
2021-02-12 08:19:30 +01:00
|
|
|
def legacy_build(
|
|
|
|
sender: UserProfile,
|
2023-04-18 17:23:58 +02:00
|
|
|
recipient_type_name: str,
|
2024-07-12 02:30:23 +02:00
|
|
|
message_to: Sequence[int] | Sequence[str],
|
|
|
|
topic_name: str | None,
|
|
|
|
realm: Realm | None = None,
|
2021-02-12 08:20:45 +01:00
|
|
|
) -> "Addressee":
|
2017-04-27 22:48:06 +02:00
|
|
|
# For legacy reason message_to used to be either a list of
|
|
|
|
# emails or a list of streams. We haven't fixed all of our
|
|
|
|
# callers yet.
|
2017-09-25 21:55:02 +02:00
|
|
|
if realm is None:
|
|
|
|
realm = sender.realm
|
|
|
|
|
2023-04-18 17:23:58 +02:00
|
|
|
if recipient_type_name == "stream":
|
2017-04-27 22:48:06 +02:00
|
|
|
if len(message_to) > 1:
|
2024-04-16 15:52:21 +02:00
|
|
|
raise JsonableError(_("Cannot send to multiple channels"))
|
2017-04-27 22:48:06 +02:00
|
|
|
|
|
|
|
if message_to:
|
2019-01-26 03:39:02 +01:00
|
|
|
stream_name_or_id = message_to[0]
|
2017-04-27 22:48:06 +02:00
|
|
|
else:
|
|
|
|
# This is a hack to deal with the fact that we still support
|
|
|
|
# default streams (and the None will be converted later in the
|
2022-02-08 00:13:33 +01:00
|
|
|
# call path).
|
2023-07-17 18:19:01 +02:00
|
|
|
if sender.default_sending_stream_id:
|
2022-02-08 00:13:33 +01:00
|
|
|
# Use the user's default stream
|
2023-07-17 18:19:01 +02:00
|
|
|
stream_name_or_id = sender.default_sending_stream_id
|
2017-09-28 21:14:08 +02:00
|
|
|
else:
|
2024-04-16 15:52:21 +02:00
|
|
|
raise JsonableError(_("Missing channel"))
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2019-03-20 22:53:26 +01:00
|
|
|
if topic_name is None:
|
|
|
|
raise JsonableError(_("Missing topic"))
|
|
|
|
|
2019-01-26 03:39:02 +01:00
|
|
|
if isinstance(stream_name_or_id, int):
|
2020-06-16 23:29:42 +02:00
|
|
|
return Addressee.for_stream_id(stream_name_or_id, topic_name)
|
2019-01-26 03:39:02 +01:00
|
|
|
|
2020-06-16 23:29:42 +02:00
|
|
|
return Addressee.for_stream_name(stream_name_or_id, topic_name)
|
2023-04-18 17:23:58 +02:00
|
|
|
elif recipient_type_name == "private":
|
2018-08-25 00:24:46 +02:00
|
|
|
if not message_to:
|
|
|
|
raise JsonableError(_("Message must have recipients"))
|
|
|
|
|
2018-08-16 21:45:10 +02:00
|
|
|
if isinstance(message_to[0], str):
|
|
|
|
emails = cast(Sequence[str], message_to)
|
|
|
|
return Addressee.for_private(emails, realm)
|
|
|
|
elif isinstance(message_to[0], int):
|
|
|
|
user_ids = cast(Sequence[int], message_to)
|
|
|
|
return Addressee.for_user_ids(user_ids=user_ids, realm=realm)
|
2017-04-27 22:48:06 +02:00
|
|
|
else:
|
|
|
|
raise JsonableError(_("Invalid message type"))
|
|
|
|
|
2019-02-07 02:05:34 +01:00
|
|
|
@staticmethod
|
2024-01-13 09:55:16 +01:00
|
|
|
def for_stream(stream: Stream, topic_name: str) -> "Addressee":
|
|
|
|
topic_name = topic_name.strip()
|
|
|
|
check_stream_topic(topic_name)
|
2019-02-07 02:05:34 +01:00
|
|
|
return Addressee(
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_type="stream",
|
2019-02-07 02:05:34 +01:00
|
|
|
stream=stream,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name=topic_name,
|
2019-02-07 02:05:34 +01:00
|
|
|
)
|
|
|
|
|
2017-04-27 22:48:06 +02:00
|
|
|
@staticmethod
|
2024-01-13 09:55:16 +01:00
|
|
|
def for_stream_name(stream_name: str, topic_name: str) -> "Addressee":
|
|
|
|
topic_name = topic_name.strip()
|
|
|
|
check_stream_topic(topic_name)
|
2017-04-27 22:48:06 +02:00
|
|
|
return Addressee(
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_type="stream",
|
2017-04-27 22:48:06 +02:00
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name=topic_name,
|
2017-04-27 22:48:06 +02:00
|
|
|
)
|
|
|
|
|
2019-01-26 03:39:02 +01:00
|
|
|
@staticmethod
|
2024-01-13 09:55:16 +01:00
|
|
|
def for_stream_id(stream_id: int, topic_name: str) -> "Addressee":
|
|
|
|
topic_name = topic_name.strip()
|
|
|
|
check_stream_topic(topic_name)
|
2019-01-26 03:39:02 +01:00
|
|
|
return Addressee(
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_type="stream",
|
2019-01-26 03:39:02 +01:00
|
|
|
stream_id=stream_id,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name=topic_name,
|
2019-01-26 03:39:02 +01:00
|
|
|
)
|
|
|
|
|
2017-04-27 22:48:06 +02:00
|
|
|
@staticmethod
|
2021-02-12 08:20:45 +01:00
|
|
|
def for_private(emails: Sequence[str], realm: Realm) -> "Addressee":
|
2018-08-25 00:24:46 +02:00
|
|
|
assert len(emails) > 0
|
2017-09-25 21:52:55 +02:00
|
|
|
user_profiles = get_user_profiles(emails, realm)
|
2017-04-27 22:48:06 +02:00
|
|
|
return Addressee(
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_type="private",
|
2017-08-18 05:01:22 +02:00
|
|
|
user_profiles=user_profiles,
|
2017-04-27 22:48:06 +02:00
|
|
|
)
|
|
|
|
|
2018-08-16 20:54:49 +02:00
|
|
|
@staticmethod
|
2021-02-12 08:20:45 +01:00
|
|
|
def for_user_ids(user_ids: Sequence[int], realm: Realm) -> "Addressee":
|
2018-08-25 00:24:46 +02:00
|
|
|
assert len(user_ids) > 0
|
2018-08-16 20:54:49 +02:00
|
|
|
user_profiles = get_user_profiles_by_ids(user_ids, realm)
|
|
|
|
return Addressee(
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_type="private",
|
2018-08-16 20:54:49 +02:00
|
|
|
user_profiles=user_profiles,
|
|
|
|
)
|
|
|
|
|
2017-04-27 22:48:06 +02:00
|
|
|
@staticmethod
|
2021-02-12 08:20:45 +01:00
|
|
|
def for_user_profile(user_profile: UserProfile) -> "Addressee":
|
2017-08-18 05:02:02 +02:00
|
|
|
user_profiles = [user_profile]
|
2017-04-27 22:48:06 +02:00
|
|
|
return Addressee(
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_type="private",
|
2017-08-18 05:01:22 +02:00
|
|
|
user_profiles=user_profiles,
|
2017-04-27 22:48:06 +02:00
|
|
|
)
|
2024-02-05 17:12:55 +01:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def for_user_profiles(user_profiles: Sequence[UserProfile]) -> "Addressee":
|
|
|
|
assert len(user_profiles) > 0
|
|
|
|
return Addressee(
|
|
|
|
msg_type="private",
|
|
|
|
user_profiles=user_profiles,
|
|
|
|
)
|