2024-07-12 02:30:25 +02:00
|
|
|
from collections.abc import Iterable
|
|
|
|
from typing import TypedDict
|
2022-07-17 13:00:21 +02:00
|
|
|
|
2022-12-04 20:51:44 +01:00
|
|
|
from zerver.lib import retention
|
2022-07-17 13:00:21 +02:00
|
|
|
from zerver.lib.retention import move_messages_to_archive
|
|
|
|
from zerver.lib.stream_subscription import get_active_subscriptions_for_stream_id
|
2024-04-27 16:51:14 +02:00
|
|
|
from zerver.models import Message, Realm, Stream, UserMessage, UserProfile
|
django_api: Extract send_event_on_commit helper.
django-stubs 4.2.1 gives transaction.on_commit a more accurate type
annotation, but this exposed that mypy can’t handle the lambda default
parameters that we use to recapture loop variables such as
for stream_id in public_stream_ids:
peer_user_ids = …
event = …
transaction.on_commit(
lambda event=event, peer_user_ids=peer_user_ids: send_event(
realm, event, peer_user_ids
)
)
https://github.com/python/mypy/issues/15459
A workaround that mypy accepts is
transaction.on_commit(
(
lambda event, peer_user_ids: lambda: send_event(
realm, event, peer_user_ids
)
)(event, peer_user_ids)
)
But that’s kind of ugly and potentially error-prone, so let’s make a
helper function for this very common pattern.
send_event_on_commit(realm, event, peer_user_ids)
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-06-17 20:53:07 +02:00
|
|
|
from zerver.tornado.django_api import send_event_on_commit
|
2022-07-17 13:00:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
class DeleteMessagesEvent(TypedDict, total=False):
|
|
|
|
type: str
|
2024-07-12 02:30:17 +02:00
|
|
|
message_ids: list[int]
|
2022-07-17 13:00:21 +02:00
|
|
|
message_type: str
|
|
|
|
topic: str
|
|
|
|
stream_id: int
|
|
|
|
|
|
|
|
|
2024-04-27 16:51:14 +02:00
|
|
|
def check_update_first_message_id(
|
2024-07-12 02:30:17 +02:00
|
|
|
realm: Realm, stream: Stream, message_ids: list[int], users_to_notify: Iterable[int]
|
2024-04-27 16:51:14 +02:00
|
|
|
) -> None:
|
|
|
|
# This will not update the `first_message_id` of streams where the
|
|
|
|
# first message was deleted prior to the implementation of this function.
|
|
|
|
assert stream.recipient_id is not None
|
|
|
|
if stream.first_message_id not in message_ids:
|
|
|
|
return
|
|
|
|
current_first_message_id = (
|
|
|
|
Message.objects.filter(realm_id=realm.id, recipient_id=stream.recipient_id)
|
|
|
|
.values_list("id", flat=True)
|
|
|
|
.order_by("id")
|
|
|
|
.first()
|
|
|
|
)
|
|
|
|
|
|
|
|
stream.first_message_id = current_first_message_id
|
|
|
|
stream.save(update_fields=["first_message_id"])
|
|
|
|
|
|
|
|
stream_event = dict(
|
|
|
|
type="stream",
|
|
|
|
op="update",
|
|
|
|
property="first_message_id",
|
|
|
|
value=stream.first_message_id,
|
|
|
|
stream_id=stream.id,
|
2024-06-03 13:49:57 +02:00
|
|
|
name=stream.name,
|
2024-04-27 16:51:14 +02:00
|
|
|
)
|
|
|
|
send_event_on_commit(realm, stream_event, users_to_notify)
|
|
|
|
|
|
|
|
|
2024-07-17 05:11:28 +02:00
|
|
|
def do_delete_messages(
|
|
|
|
realm: Realm, messages: Iterable[Message], *, acting_user: UserProfile | None
|
|
|
|
) -> None:
|
2022-07-17 13:00:21 +02:00
|
|
|
# messages in delete_message event belong to the same topic
|
2023-06-19 16:42:11 +02:00
|
|
|
# or is a single direct message, as any other behaviour is not possible with
|
2022-07-17 13:00:21 +02:00
|
|
|
# the current callers to this method.
|
|
|
|
messages = list(messages)
|
|
|
|
message_ids = [message.id for message in messages]
|
|
|
|
if not message_ids:
|
|
|
|
return
|
|
|
|
|
|
|
|
event: DeleteMessagesEvent = {
|
|
|
|
"type": "delete_message",
|
|
|
|
"message_ids": message_ids,
|
|
|
|
}
|
|
|
|
|
|
|
|
sample_message = messages[0]
|
|
|
|
message_type = "stream"
|
2024-07-17 05:11:28 +02:00
|
|
|
users_to_notify = set()
|
2022-07-17 13:00:21 +02:00
|
|
|
if not sample_message.is_stream_message():
|
|
|
|
assert len(messages) == 1
|
|
|
|
message_type = "private"
|
|
|
|
ums = UserMessage.objects.filter(message_id__in=message_ids)
|
2024-07-17 05:11:28 +02:00
|
|
|
users_to_notify = set(ums.values_list("user_profile_id", flat=True))
|
2022-07-17 13:00:21 +02:00
|
|
|
archiving_chunk_size = retention.MESSAGE_BATCH_SIZE
|
|
|
|
|
|
|
|
if message_type == "stream":
|
|
|
|
stream_id = sample_message.recipient.type_id
|
|
|
|
event["stream_id"] = stream_id
|
|
|
|
event["topic"] = sample_message.topic_name()
|
|
|
|
subscriptions = get_active_subscriptions_for_stream_id(
|
|
|
|
stream_id, include_deactivated_users=False
|
|
|
|
)
|
|
|
|
# We exclude long-term idle users, since they by definition have no active clients.
|
|
|
|
subscriptions = subscriptions.exclude(user_profile__long_term_idle=True)
|
2024-07-17 05:11:28 +02:00
|
|
|
users_to_notify = set(subscriptions.values_list("user_profile_id", flat=True))
|
2022-07-17 13:00:21 +02:00
|
|
|
archiving_chunk_size = retention.STREAM_MESSAGE_BATCH_SIZE
|
|
|
|
|
2024-07-17 05:11:28 +02:00
|
|
|
if acting_user is not None:
|
|
|
|
# Always send event to the user who deleted the message.
|
|
|
|
users_to_notify.add(acting_user.id)
|
|
|
|
|
2022-07-17 13:00:21 +02:00
|
|
|
move_messages_to_archive(message_ids, realm=realm, chunk_size=archiving_chunk_size)
|
2024-04-27 16:51:14 +02:00
|
|
|
if message_type == "stream":
|
|
|
|
stream = Stream.objects.get(id=sample_message.recipient.type_id)
|
|
|
|
check_update_first_message_id(realm, stream, message_ids, users_to_notify)
|
2022-07-17 13:00:21 +02:00
|
|
|
|
|
|
|
event["message_type"] = message_type
|
django_api: Extract send_event_on_commit helper.
django-stubs 4.2.1 gives transaction.on_commit a more accurate type
annotation, but this exposed that mypy can’t handle the lambda default
parameters that we use to recapture loop variables such as
for stream_id in public_stream_ids:
peer_user_ids = …
event = …
transaction.on_commit(
lambda event=event, peer_user_ids=peer_user_ids: send_event(
realm, event, peer_user_ids
)
)
https://github.com/python/mypy/issues/15459
A workaround that mypy accepts is
transaction.on_commit(
(
lambda event, peer_user_ids: lambda: send_event(
realm, event, peer_user_ids
)
)(event, peer_user_ids)
)
But that’s kind of ugly and potentially error-prone, so let’s make a
helper function for this very common pattern.
send_event_on_commit(realm, event, peer_user_ids)
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-06-17 20:53:07 +02:00
|
|
|
send_event_on_commit(realm, event, users_to_notify)
|
2022-07-17 13:00:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
def do_delete_messages_by_sender(user: UserProfile) -> None:
|
|
|
|
message_ids = list(
|
2023-08-30 21:19:37 +02:00
|
|
|
# Uses index: zerver_message_realm_sender_recipient (prefix)
|
|
|
|
Message.objects.filter(realm_id=user.realm_id, sender=user)
|
|
|
|
.values_list("id", flat=True)
|
|
|
|
.order_by("id")
|
2022-07-17 13:00:21 +02:00
|
|
|
)
|
|
|
|
if message_ids:
|
|
|
|
move_messages_to_archive(message_ids, chunk_size=retention.STREAM_MESSAGE_BATCH_SIZE)
|