2024-08-18 14:16:43 +02:00
|
|
|
from typing import Annotated
|
|
|
|
|
2023-04-14 21:19:46 +02:00
|
|
|
from django.http import HttpRequest, HttpResponse
|
2023-04-20 04:26:41 +02:00
|
|
|
from django.utils.timezone import now as timezone_now
|
|
|
|
from django.utils.translation import gettext as _
|
2024-08-18 14:16:43 +02:00
|
|
|
from pydantic import Json, NonNegativeInt
|
2023-04-14 21:19:46 +02:00
|
|
|
|
2023-04-20 04:26:41 +02:00
|
|
|
from zerver.actions.scheduled_messages import (
|
|
|
|
check_schedule_message,
|
|
|
|
delete_scheduled_message,
|
2023-05-16 21:18:09 +02:00
|
|
|
edit_scheduled_message,
|
2023-04-20 04:26:41 +02:00
|
|
|
)
|
|
|
|
from zerver.lib.exceptions import JsonableError
|
2023-10-07 10:22:37 +02:00
|
|
|
from zerver.lib.recipient_parsing import extract_direct_message_recipient_ids, extract_stream_id
|
2024-08-18 14:16:43 +02:00
|
|
|
from zerver.lib.request import RequestNotes
|
2023-04-14 21:19:46 +02:00
|
|
|
from zerver.lib.response import json_success
|
2023-04-21 13:23:10 +02:00
|
|
|
from zerver.lib.scheduled_messages import get_undelivered_scheduled_messages
|
2023-04-20 04:26:41 +02:00
|
|
|
from zerver.lib.timestamp import timestamp_to_datetime
|
2024-08-18 14:16:43 +02:00
|
|
|
from zerver.lib.typed_endpoint import (
|
|
|
|
ApiParamConfig,
|
|
|
|
OptionalTopic,
|
|
|
|
PathOnly,
|
|
|
|
typed_endpoint,
|
|
|
|
typed_endpoint_without_parameters,
|
|
|
|
)
|
|
|
|
from zerver.lib.typed_endpoint_validators import check_string_in_validator
|
2023-04-20 04:26:41 +02:00
|
|
|
from zerver.models import Message, UserProfile
|
2023-04-14 21:19:46 +02:00
|
|
|
|
|
|
|
|
2024-08-18 14:16:43 +02:00
|
|
|
@typed_endpoint_without_parameters
|
2023-04-14 21:19:46 +02:00
|
|
|
def fetch_scheduled_messages(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
2023-04-21 13:23:10 +02:00
|
|
|
return json_success(
|
|
|
|
request, data={"scheduled_messages": get_undelivered_scheduled_messages(user_profile)}
|
|
|
|
)
|
2023-04-14 21:19:46 +02:00
|
|
|
|
|
|
|
|
2024-08-18 14:16:43 +02:00
|
|
|
@typed_endpoint
|
2023-04-14 21:19:46 +02:00
|
|
|
def delete_scheduled_messages(
|
2023-05-31 15:28:20 +02:00
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2024-08-18 14:16:43 +02:00
|
|
|
*,
|
|
|
|
scheduled_message_id: PathOnly[NonNegativeInt],
|
2023-04-14 21:19:46 +02:00
|
|
|
) -> HttpResponse:
|
2023-04-17 15:02:07 +02:00
|
|
|
delete_scheduled_message(user_profile, scheduled_message_id)
|
2023-04-14 21:19:46 +02:00
|
|
|
return json_success(request)
|
2023-04-20 04:26:41 +02:00
|
|
|
|
|
|
|
|
2024-08-18 14:16:43 +02:00
|
|
|
@typed_endpoint
|
2023-05-16 21:18:09 +02:00
|
|
|
def update_scheduled_message_backend(
|
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2024-08-18 14:16:43 +02:00
|
|
|
*,
|
|
|
|
scheduled_message_id: PathOnly[NonNegativeInt],
|
|
|
|
req_type: Annotated[
|
|
|
|
Annotated[str, check_string_in_validator(Message.API_RECIPIENT_TYPES)] | None,
|
|
|
|
ApiParamConfig("type"),
|
|
|
|
] = None,
|
|
|
|
to: Json[int | list[int]] | None = None,
|
|
|
|
topic_name: OptionalTopic = None,
|
|
|
|
message_content: Annotated[str | None, ApiParamConfig("content")] = None,
|
|
|
|
scheduled_delivery_timestamp: Json[int] | None = None,
|
2023-05-16 21:18:09 +02:00
|
|
|
) -> HttpResponse:
|
|
|
|
if (
|
|
|
|
req_type is None
|
2024-08-18 14:16:43 +02:00
|
|
|
and to is None
|
2023-05-16 21:18:09 +02:00
|
|
|
and topic_name is None
|
|
|
|
and message_content is None
|
|
|
|
and scheduled_delivery_timestamp is None
|
|
|
|
):
|
|
|
|
raise JsonableError(_("Nothing to change"))
|
|
|
|
|
|
|
|
recipient_type_name = None
|
|
|
|
if req_type:
|
2024-08-18 14:16:43 +02:00
|
|
|
if to is None:
|
2023-05-16 21:18:09 +02:00
|
|
|
raise JsonableError(_("Recipient required when updating type of scheduled message."))
|
|
|
|
else:
|
|
|
|
recipient_type_name = req_type
|
|
|
|
|
2024-04-10 20:48:10 +02:00
|
|
|
if recipient_type_name is not None and recipient_type_name == "channel":
|
|
|
|
# For now, use "stream" from Message.API_RECIPIENT_TYPES.
|
|
|
|
# TODO: Use "channel" here, as well as in events and
|
|
|
|
# message (created, schdeduled, drafts) objects/dicts.
|
|
|
|
recipient_type_name = "stream"
|
|
|
|
|
2023-05-16 21:18:09 +02:00
|
|
|
if recipient_type_name is not None and recipient_type_name == "stream" and topic_name is None:
|
2024-04-16 15:52:21 +02:00
|
|
|
raise JsonableError(_("Topic required when updating scheduled message type to channel."))
|
2023-05-16 21:18:09 +02:00
|
|
|
|
|
|
|
if recipient_type_name is not None and recipient_type_name == "direct":
|
|
|
|
# For now, use "private" from Message.API_RECIPIENT_TYPES.
|
|
|
|
# TODO: Use "direct" here, as well as in events and
|
|
|
|
# scheduled message objects/dicts.
|
|
|
|
recipient_type_name = "private"
|
|
|
|
|
|
|
|
message_to = None
|
2024-08-18 14:16:43 +02:00
|
|
|
if to is not None:
|
2023-05-16 21:18:09 +02:00
|
|
|
# Because the recipient_type_name may not be updated/changed,
|
|
|
|
# we extract these updated recipient IDs in edit_scheduled_message.
|
2024-08-18 14:16:43 +02:00
|
|
|
message_to = to
|
2023-05-16 21:18:09 +02:00
|
|
|
|
|
|
|
deliver_at = None
|
|
|
|
if scheduled_delivery_timestamp is not None:
|
|
|
|
deliver_at = timestamp_to_datetime(scheduled_delivery_timestamp)
|
|
|
|
if deliver_at <= timezone_now():
|
|
|
|
raise JsonableError(_("Scheduled delivery time must be in the future."))
|
|
|
|
|
|
|
|
sender = user_profile
|
|
|
|
client = RequestNotes.get_notes(request).client
|
|
|
|
assert client is not None
|
|
|
|
|
|
|
|
edit_scheduled_message(
|
|
|
|
sender,
|
|
|
|
client,
|
|
|
|
scheduled_message_id,
|
|
|
|
recipient_type_name,
|
|
|
|
message_to,
|
|
|
|
topic_name,
|
|
|
|
message_content,
|
|
|
|
deliver_at,
|
|
|
|
realm=user_profile.realm,
|
|
|
|
)
|
|
|
|
|
|
|
|
return json_success(request)
|
|
|
|
|
|
|
|
|
2024-08-18 14:16:43 +02:00
|
|
|
@typed_endpoint
|
2023-05-16 21:18:09 +02:00
|
|
|
def create_scheduled_message_backend(
|
2023-04-20 04:26:41 +02:00
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2024-08-18 14:16:43 +02:00
|
|
|
*,
|
|
|
|
req_type: Annotated[
|
|
|
|
Annotated[str, check_string_in_validator(Message.API_RECIPIENT_TYPES)],
|
|
|
|
ApiParamConfig("type"),
|
|
|
|
],
|
|
|
|
req_to: Annotated[Json[int | list[int]], ApiParamConfig("to")],
|
|
|
|
message_content: Annotated[str, ApiParamConfig("content")],
|
|
|
|
scheduled_delivery_timestamp: Json[int],
|
|
|
|
topic_name: OptionalTopic = None,
|
|
|
|
read_by_sender: Json[bool] | None = None,
|
2023-04-20 04:26:41 +02:00
|
|
|
) -> HttpResponse:
|
|
|
|
recipient_type_name = req_type
|
|
|
|
if recipient_type_name == "direct":
|
|
|
|
# For now, use "private" from Message.API_RECIPIENT_TYPES.
|
|
|
|
# TODO: Use "direct" here, as well as in events and
|
|
|
|
# scheduled message objects/dicts.
|
|
|
|
recipient_type_name = "private"
|
2024-04-10 20:48:10 +02:00
|
|
|
elif recipient_type_name == "channel":
|
|
|
|
# For now, use "stream" from Message.API_RECIPIENT_TYPES.
|
|
|
|
# TODO: Use "channel" here, as well as in events and
|
|
|
|
# message (created, schdeduled, drafts) objects/dicts.
|
|
|
|
recipient_type_name = "stream"
|
2023-04-20 04:26:41 +02:00
|
|
|
|
|
|
|
deliver_at = timestamp_to_datetime(scheduled_delivery_timestamp)
|
|
|
|
if deliver_at <= timezone_now():
|
|
|
|
raise JsonableError(_("Scheduled delivery time must be in the future."))
|
|
|
|
|
|
|
|
sender = user_profile
|
|
|
|
client = RequestNotes.get_notes(request).client
|
|
|
|
assert client is not None
|
|
|
|
|
|
|
|
if recipient_type_name == "stream":
|
2023-10-07 10:22:37 +02:00
|
|
|
stream_id = extract_stream_id(req_to)
|
|
|
|
message_to = [stream_id]
|
2023-04-20 04:26:41 +02:00
|
|
|
else:
|
2023-05-09 20:45:42 +02:00
|
|
|
message_to = extract_direct_message_recipient_ids(req_to)
|
2023-04-20 04:26:41 +02:00
|
|
|
|
|
|
|
scheduled_message_id = check_schedule_message(
|
|
|
|
sender,
|
|
|
|
client,
|
|
|
|
recipient_type_name,
|
|
|
|
message_to,
|
|
|
|
topic_name,
|
|
|
|
message_content,
|
|
|
|
deliver_at,
|
|
|
|
realm=user_profile.realm,
|
|
|
|
forwarder_user_profile=user_profile,
|
2023-12-14 07:11:00 +01:00
|
|
|
read_by_sender=read_by_sender,
|
2023-04-20 04:26:41 +02:00
|
|
|
)
|
|
|
|
return json_success(request, data={"scheduled_message_id": scheduled_message_id})
|