mirror of https://github.com/zulip/zulip.git
message_send: Add read_by_sender API parameter.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
d7d5b6c73e
commit
77a6f44455
|
@ -20,6 +20,14 @@ format used by the Zulip server that they are interacting with.
|
||||||
|
|
||||||
## Changes in Zulip 8.0
|
## Changes in Zulip 8.0
|
||||||
|
|
||||||
|
**Feature level 236**
|
||||||
|
|
||||||
|
* [`POST /messages`](/api/send-message), [`POST
|
||||||
|
/scheduled_messages`](/api/create-scheduled-message): The new
|
||||||
|
`read_by_sender` parameter lets the client override the heuristic
|
||||||
|
that determines whether the new message will be initially marked
|
||||||
|
read by its sender.
|
||||||
|
|
||||||
**Feature level 235**
|
**Feature level 235**
|
||||||
|
|
||||||
* [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults),
|
* [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults),
|
||||||
|
|
|
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
||||||
# Changes should be accompanied by documentation explaining what the
|
# Changes should be accompanied by documentation explaining what the
|
||||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
API_FEATURE_LEVEL = 235
|
API_FEATURE_LEVEL = 236
|
||||||
|
|
||||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
||||||
# only when going from an old version of the code to a newer version. Bump
|
# only when going from an old version of the code to a newer version. Bump
|
||||||
|
|
|
@ -732,13 +732,11 @@ def create_user_messages(
|
||||||
followed_topic_email_user_ids: AbstractSet[int],
|
followed_topic_email_user_ids: AbstractSet[int],
|
||||||
mark_as_read_user_ids: Set[int],
|
mark_as_read_user_ids: Set[int],
|
||||||
limit_unread_user_ids: Optional[Set[int]],
|
limit_unread_user_ids: Optional[Set[int]],
|
||||||
scheduled_message_to_self: bool,
|
|
||||||
topic_participant_user_ids: Set[int],
|
topic_participant_user_ids: Set[int],
|
||||||
) -> List[UserMessageLite]:
|
) -> List[UserMessageLite]:
|
||||||
# These properties on the Message are set via
|
# These properties on the Message are set via
|
||||||
# render_markdown by code in the Markdown inline patterns
|
# render_markdown by code in the Markdown inline patterns
|
||||||
ids_with_alert_words = rendering_result.user_ids_with_alert_words
|
ids_with_alert_words = rendering_result.user_ids_with_alert_words
|
||||||
sender_id = message.sender.id
|
|
||||||
is_stream_message = message.is_stream_message()
|
is_stream_message = message.is_stream_message()
|
||||||
|
|
||||||
base_flags = 0
|
base_flags = 0
|
||||||
|
@ -769,17 +767,8 @@ def create_user_messages(
|
||||||
user_messages = []
|
user_messages = []
|
||||||
for user_profile_id in um_eligible_user_ids:
|
for user_profile_id in um_eligible_user_ids:
|
||||||
flags = base_flags
|
flags = base_flags
|
||||||
if (
|
if user_profile_id in mark_as_read_user_ids or (
|
||||||
(
|
limit_unread_user_ids is not None and user_profile_id not in limit_unread_user_ids
|
||||||
# Messages you sent from a non-API client are
|
|
||||||
# automatically marked as read for yourself; scheduled
|
|
||||||
# messages to yourself only are not.
|
|
||||||
user_profile_id == sender_id
|
|
||||||
and message.sending_client.default_read_by_sender()
|
|
||||||
and not scheduled_message_to_self
|
|
||||||
)
|
|
||||||
or user_profile_id in mark_as_read_user_ids
|
|
||||||
or (limit_unread_user_ids is not None and user_profile_id not in limit_unread_user_ids)
|
|
||||||
):
|
):
|
||||||
flags |= UserMessage.flags.read
|
flags |= UserMessage.flags.read
|
||||||
if user_profile_id in mentioned_user_ids:
|
if user_profile_id in mentioned_user_ids:
|
||||||
|
@ -865,7 +854,6 @@ def do_send_messages(
|
||||||
send_message_requests_maybe_none: Sequence[Optional[SendMessageRequest]],
|
send_message_requests_maybe_none: Sequence[Optional[SendMessageRequest]],
|
||||||
*,
|
*,
|
||||||
email_gateway: bool = False,
|
email_gateway: bool = False,
|
||||||
scheduled_message_to_self: bool = False,
|
|
||||||
mark_as_read: Sequence[int] = [],
|
mark_as_read: Sequence[int] = [],
|
||||||
) -> List[SentMessageResult]:
|
) -> List[SentMessageResult]:
|
||||||
"""See
|
"""See
|
||||||
|
@ -915,7 +903,6 @@ def do_send_messages(
|
||||||
followed_topic_email_user_ids=send_request.followed_topic_email_user_ids,
|
followed_topic_email_user_ids=send_request.followed_topic_email_user_ids,
|
||||||
mark_as_read_user_ids=mark_as_read_user_ids,
|
mark_as_read_user_ids=mark_as_read_user_ids,
|
||||||
limit_unread_user_ids=send_request.limit_unread_user_ids,
|
limit_unread_user_ids=send_request.limit_unread_user_ids,
|
||||||
scheduled_message_to_self=scheduled_message_to_self,
|
|
||||||
topic_participant_user_ids=send_request.topic_participant_user_ids,
|
topic_participant_user_ids=send_request.topic_participant_user_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1345,11 +1332,15 @@ def check_send_stream_message(
|
||||||
stream_name: str,
|
stream_name: str,
|
||||||
topic: str,
|
topic: str,
|
||||||
body: str,
|
body: str,
|
||||||
|
*,
|
||||||
realm: Optional[Realm] = None,
|
realm: Optional[Realm] = None,
|
||||||
|
read_by_sender: bool = False,
|
||||||
) -> int:
|
) -> int:
|
||||||
addressee = Addressee.for_stream_name(stream_name, topic)
|
addressee = Addressee.for_stream_name(stream_name, topic)
|
||||||
message = check_message(sender, client, addressee, body, realm)
|
message = check_message(sender, client, addressee, body, realm)
|
||||||
sent_message_result = do_send_messages([message])[0]
|
sent_message_result = do_send_messages(
|
||||||
|
[message], mark_as_read=[sender.id] if read_by_sender else []
|
||||||
|
)[0]
|
||||||
return sent_message_result.message_id
|
return sent_message_result.message_id
|
||||||
|
|
||||||
|
|
||||||
|
@ -1394,6 +1385,7 @@ def check_send_message(
|
||||||
widget_content: Optional[str] = None,
|
widget_content: Optional[str] = None,
|
||||||
*,
|
*,
|
||||||
skip_stream_access_check: bool = False,
|
skip_stream_access_check: bool = False,
|
||||||
|
read_by_sender: bool = False,
|
||||||
) -> SentMessageResult:
|
) -> SentMessageResult:
|
||||||
addressee = Addressee.legacy_build(sender, recipient_type_name, message_to, topic_name)
|
addressee = Addressee.legacy_build(sender, recipient_type_name, message_to, topic_name)
|
||||||
try:
|
try:
|
||||||
|
@ -1413,7 +1405,7 @@ def check_send_message(
|
||||||
)
|
)
|
||||||
except ZephyrMessageAlreadySentError as e:
|
except ZephyrMessageAlreadySentError as e:
|
||||||
return SentMessageResult(message_id=e.message_id)
|
return SentMessageResult(message_id=e.message_id)
|
||||||
return do_send_messages([message])[0]
|
return do_send_messages([message], mark_as_read=[sender.id] if read_by_sender else [])[0]
|
||||||
|
|
||||||
|
|
||||||
def send_rate_limited_pm_notification_to_bot_owner(
|
def send_rate_limited_pm_notification_to_bot_owner(
|
||||||
|
|
|
@ -43,7 +43,9 @@ def check_schedule_message(
|
||||||
message_content: str,
|
message_content: str,
|
||||||
deliver_at: datetime,
|
deliver_at: datetime,
|
||||||
realm: Optional[Realm] = None,
|
realm: Optional[Realm] = None,
|
||||||
|
*,
|
||||||
forwarder_user_profile: Optional[UserProfile] = None,
|
forwarder_user_profile: Optional[UserProfile] = None,
|
||||||
|
read_by_sender: Optional[bool] = None,
|
||||||
) -> int:
|
) -> int:
|
||||||
addressee = Addressee.legacy_build(sender, recipient_type_name, message_to, topic_name)
|
addressee = Addressee.legacy_build(sender, recipient_type_name, message_to, topic_name)
|
||||||
send_request = check_message(
|
send_request = check_message(
|
||||||
|
@ -56,11 +58,22 @@ def check_schedule_message(
|
||||||
)
|
)
|
||||||
send_request.deliver_at = deliver_at
|
send_request.deliver_at = deliver_at
|
||||||
|
|
||||||
return do_schedule_messages([send_request], sender)[0]
|
if read_by_sender is None:
|
||||||
|
# Legacy default: a scheduled message you sent from a non-API client is
|
||||||
|
# automatically marked as read for yourself, unless it was sent to
|
||||||
|
# yourself only.
|
||||||
|
read_by_sender = (
|
||||||
|
client.default_read_by_sender() and send_request.message.recipient != sender.recipient
|
||||||
|
)
|
||||||
|
|
||||||
|
return do_schedule_messages([send_request], sender, read_by_sender=read_by_sender)[0]
|
||||||
|
|
||||||
|
|
||||||
def do_schedule_messages(
|
def do_schedule_messages(
|
||||||
send_message_requests: Sequence[SendMessageRequest], sender: UserProfile
|
send_message_requests: Sequence[SendMessageRequest],
|
||||||
|
sender: UserProfile,
|
||||||
|
*,
|
||||||
|
read_by_sender: bool = False,
|
||||||
) -> List[int]:
|
) -> List[int]:
|
||||||
scheduled_messages: List[Tuple[ScheduledMessage, SendMessageRequest]] = []
|
scheduled_messages: List[Tuple[ScheduledMessage, SendMessageRequest]] = []
|
||||||
|
|
||||||
|
@ -80,6 +93,7 @@ def do_schedule_messages(
|
||||||
scheduled_message.realm = send_request.realm
|
scheduled_message.realm = send_request.realm
|
||||||
assert send_request.deliver_at is not None
|
assert send_request.deliver_at is not None
|
||||||
scheduled_message.scheduled_timestamp = send_request.deliver_at
|
scheduled_message.scheduled_timestamp = send_request.deliver_at
|
||||||
|
scheduled_message.read_by_sender = read_by_sender
|
||||||
scheduled_message.delivery_type = ScheduledMessage.SEND_LATER
|
scheduled_message.delivery_type = ScheduledMessage.SEND_LATER
|
||||||
|
|
||||||
scheduled_messages.append((scheduled_message, send_request))
|
scheduled_messages.append((scheduled_message, send_request))
|
||||||
|
@ -301,9 +315,19 @@ def send_scheduled_message(scheduled_message: ScheduledMessage) -> None:
|
||||||
scheduled_message.realm,
|
scheduled_message.realm,
|
||||||
)
|
)
|
||||||
|
|
||||||
scheduled_message_to_self = scheduled_message.recipient == scheduled_message.sender.recipient
|
read_by_sender = scheduled_message.read_by_sender
|
||||||
|
if read_by_sender is None: # nocoverage
|
||||||
|
# Legacy default: a scheduled message you sent from a non-API client is
|
||||||
|
# automatically marked as read for yourself, unless it was sent to
|
||||||
|
# yourself only.
|
||||||
|
read_by_sender = (
|
||||||
|
scheduled_message.sending_client.default_read_by_sender()
|
||||||
|
and scheduled_message.recipient != scheduled_message.sender.recipient
|
||||||
|
)
|
||||||
|
|
||||||
sent_message_result = do_send_messages(
|
sent_message_result = do_send_messages(
|
||||||
[send_request], scheduled_message_to_self=scheduled_message_to_self
|
[send_request],
|
||||||
|
mark_as_read=[scheduled_message.sender_id] if read_by_sender else [],
|
||||||
)[0]
|
)[0]
|
||||||
scheduled_message.delivered_message_id = sent_message_result.message_id
|
scheduled_message.delivered_message_id = sent_message_result.message_id
|
||||||
scheduled_message.delivered = True
|
scheduled_message.delivered = True
|
||||||
|
|
|
@ -1075,10 +1075,11 @@ Output:
|
||||||
from_user: UserProfile,
|
from_user: UserProfile,
|
||||||
to_user: UserProfile,
|
to_user: UserProfile,
|
||||||
content: str = "test content",
|
content: str = "test content",
|
||||||
sending_client_name: str = "test suite",
|
*,
|
||||||
|
read_by_sender: bool = True,
|
||||||
) -> int:
|
) -> int:
|
||||||
recipient_list = [to_user.id]
|
recipient_list = [to_user.id]
|
||||||
(sending_client, _) = Client.objects.get_or_create(name=sending_client_name)
|
(sending_client, _) = Client.objects.get_or_create(name="test suite")
|
||||||
|
|
||||||
sent_message_result = check_send_message(
|
sent_message_result = check_send_message(
|
||||||
from_user,
|
from_user,
|
||||||
|
@ -1087,6 +1088,7 @@ Output:
|
||||||
recipient_list,
|
recipient_list,
|
||||||
None,
|
None,
|
||||||
content,
|
content,
|
||||||
|
read_by_sender=read_by_sender,
|
||||||
)
|
)
|
||||||
return sent_message_result.message_id
|
return sent_message_result.message_id
|
||||||
|
|
||||||
|
@ -1095,12 +1097,13 @@ Output:
|
||||||
from_user: UserProfile,
|
from_user: UserProfile,
|
||||||
to_users: List[UserProfile],
|
to_users: List[UserProfile],
|
||||||
content: str = "test content",
|
content: str = "test content",
|
||||||
sending_client_name: str = "test suite",
|
*,
|
||||||
|
read_by_sender: bool = True,
|
||||||
) -> int:
|
) -> int:
|
||||||
to_user_ids = [u.id for u in to_users]
|
to_user_ids = [u.id for u in to_users]
|
||||||
assert len(to_user_ids) >= 2
|
assert len(to_user_ids) >= 2
|
||||||
|
|
||||||
(sending_client, _) = Client.objects.get_or_create(name=sending_client_name)
|
(sending_client, _) = Client.objects.get_or_create(name="test suite")
|
||||||
|
|
||||||
sent_message_result = check_send_message(
|
sent_message_result = check_send_message(
|
||||||
from_user,
|
from_user,
|
||||||
|
@ -1109,6 +1112,7 @@ Output:
|
||||||
to_user_ids,
|
to_user_ids,
|
||||||
None,
|
None,
|
||||||
content,
|
content,
|
||||||
|
read_by_sender=read_by_sender,
|
||||||
)
|
)
|
||||||
return sent_message_result.message_id
|
return sent_message_result.message_id
|
||||||
|
|
||||||
|
@ -1119,10 +1123,11 @@ Output:
|
||||||
content: str = "test content",
|
content: str = "test content",
|
||||||
topic_name: str = "test",
|
topic_name: str = "test",
|
||||||
recipient_realm: Optional[Realm] = None,
|
recipient_realm: Optional[Realm] = None,
|
||||||
sending_client_name: str = "test suite",
|
*,
|
||||||
allow_unsubscribed_sender: bool = False,
|
allow_unsubscribed_sender: bool = False,
|
||||||
|
read_by_sender: bool = True,
|
||||||
) -> int:
|
) -> int:
|
||||||
(sending_client, _) = Client.objects.get_or_create(name=sending_client_name)
|
(sending_client, _) = Client.objects.get_or_create(name="test suite")
|
||||||
|
|
||||||
message_id = check_send_stream_message(
|
message_id = check_send_stream_message(
|
||||||
sender=sender,
|
sender=sender,
|
||||||
|
@ -1131,6 +1136,7 @@ Output:
|
||||||
topic=topic_name,
|
topic=topic_name,
|
||||||
body=content,
|
body=content,
|
||||||
realm=recipient_realm,
|
realm=recipient_realm,
|
||||||
|
read_by_sender=read_by_sender,
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
not UserMessage.objects.filter(user_profile=sender, message_id=message_id).exists()
|
not UserMessage.objects.filter(user_profile=sender, message_id=message_id).exists()
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.2.8 on 2023-12-14 00:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0494_realmuserdefault_automatically_follow_topics_where_mentioned_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="scheduledmessage",
|
||||||
|
name="read_by_sender",
|
||||||
|
field=models.BooleanField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -4629,6 +4629,7 @@ class ScheduledMessage(models.Model):
|
||||||
stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE)
|
stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE)
|
||||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||||
scheduled_timestamp = models.DateTimeField(db_index=True)
|
scheduled_timestamp = models.DateTimeField(db_index=True)
|
||||||
|
read_by_sender = models.BooleanField(null=True)
|
||||||
delivered = models.BooleanField(default=False)
|
delivered = models.BooleanField(default=False)
|
||||||
delivered_message = models.ForeignKey(Message, null=True, on_delete=CASCADE)
|
delivered_message = models.ForeignKey(Message, null=True, on_delete=CASCADE)
|
||||||
has_attachment = models.BooleanField(default=False, db_index=True)
|
has_attachment = models.BooleanField(default=False, db_index=True)
|
||||||
|
|
|
@ -5606,6 +5606,17 @@ paths:
|
||||||
in UTC seconds.
|
in UTC seconds.
|
||||||
required: true
|
required: true
|
||||||
example: 3165826990
|
example: 3165826990
|
||||||
|
- name: read_by_sender
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether the message should be initially marked read by its
|
||||||
|
sender. If unspecified, the server uses a heuristic based
|
||||||
|
on the client name and the recipient.
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 8.0 (feature level 236).
|
||||||
|
example: true
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Success.
|
description: Success.
|
||||||
|
@ -6373,6 +6384,17 @@ paths:
|
||||||
chosen freely by the client; the server will pass it back to the client without
|
chosen freely by the client; the server will pass it back to the client without
|
||||||
inspecting it, as described in the `queue_id` description.
|
inspecting it, as described in the `queue_id` description.
|
||||||
example: "100.01"
|
example: "100.01"
|
||||||
|
- name: read_by_sender
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Whether the message should be initially marked read by its
|
||||||
|
sender. If unspecified, the server uses a heuristic based
|
||||||
|
on the client name.
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 8.0 (feature level 236).
|
||||||
|
example: true
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Success.
|
description: Success.
|
||||||
|
|
|
@ -33,7 +33,6 @@ from zerver.models import (
|
||||||
Stream,
|
Stream,
|
||||||
UserActivityInterval,
|
UserActivityInterval,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
get_client,
|
|
||||||
get_realm,
|
get_realm,
|
||||||
get_stream,
|
get_stream,
|
||||||
)
|
)
|
||||||
|
@ -547,8 +546,6 @@ class TestDigestEmailMessages(ZulipTestCase):
|
||||||
self.assertEqual(stream_info["html"], [])
|
self.assertEqual(stream_info["html"], [])
|
||||||
|
|
||||||
def simulate_stream_conversation(self, stream: str, senders: List[str]) -> List[int]:
|
def simulate_stream_conversation(self, stream: str, senders: List[str]) -> List[int]:
|
||||||
client = "website" # this makes `default_read_by_sender` return True
|
|
||||||
sending_client = get_client(client)
|
|
||||||
message_ids = [] # List[int]
|
message_ids = [] # List[int]
|
||||||
for sender_name in senders:
|
for sender_name in senders:
|
||||||
sender = self.example_user(sender_name)
|
sender = self.example_user(sender_name)
|
||||||
|
@ -556,7 +553,6 @@ class TestDigestEmailMessages(ZulipTestCase):
|
||||||
content = f"some content for {stream} from {sender_name}"
|
content = f"some content for {stream} from {sender_name}"
|
||||||
message_id = self.send_stream_message(sender, stream, content)
|
message_id = self.send_stream_message(sender, stream, content)
|
||||||
message_ids.append(message_id)
|
message_ids.append(message_id)
|
||||||
Message.objects.filter(id__in=message_ids).update(sending_client=sending_client)
|
|
||||||
return message_ids
|
return message_ids
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -874,16 +874,11 @@ class GetUnreadMsgsTest(ZulipTestCase):
|
||||||
message_id = self.send_personal_message(
|
message_id = self.send_personal_message(
|
||||||
from_user=hamlet,
|
from_user=hamlet,
|
||||||
to_user=other_user,
|
to_user=other_user,
|
||||||
sending_client_name="some_api_program",
|
read_by_sender=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check our test setup is correct--the message should
|
|
||||||
# not have looked like it was sent by a human.
|
|
||||||
message = Message.objects.get(id=message_id)
|
message = Message.objects.get(id=message_id)
|
||||||
self.assertFalse(message.sending_client.default_read_by_sender())
|
|
||||||
|
|
||||||
# And since it was not sent by a human, it should not
|
# This message should not be read, not even by the sender (Hamlet).
|
||||||
# be read, not even by the sender (Hamlet).
|
|
||||||
um = UserMessage.objects.get(
|
um = UserMessage.objects.get(
|
||||||
user_profile_id=hamlet.id,
|
user_profile_id=hamlet.id,
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
|
|
|
@ -19,7 +19,7 @@ from zerver.lib.message import render_markdown
|
||||||
from zerver.lib.request import REQ, RequestNotes, has_request_variables
|
from zerver.lib.request import REQ, RequestNotes, has_request_variables
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.topic import REQ_topic
|
from zerver.lib.topic import REQ_topic
|
||||||
from zerver.lib.validator import check_string_in, to_float
|
from zerver.lib.validator import check_bool, check_string_in, to_float
|
||||||
from zerver.lib.zcommand import process_zcommands
|
from zerver.lib.zcommand import process_zcommands
|
||||||
from zerver.lib.zephyr import compute_mit_user_fullname
|
from zerver.lib.zephyr import compute_mit_user_fullname
|
||||||
from zerver.models import Client, Message, RealmDomain, UserProfile, get_user_including_cross_realm
|
from zerver.models import Client, Message, RealmDomain, UserProfile, get_user_including_cross_realm
|
||||||
|
@ -136,6 +136,7 @@ def send_message_backend(
|
||||||
local_id: Optional[str] = REQ(default=None),
|
local_id: Optional[str] = REQ(default=None),
|
||||||
queue_id: Optional[str] = REQ(default=None),
|
queue_id: Optional[str] = REQ(default=None),
|
||||||
time: Optional[float] = REQ(default=None, converter=to_float, documentation_pending=True),
|
time: Optional[float] = REQ(default=None, converter=to_float, documentation_pending=True),
|
||||||
|
read_by_sender: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
recipient_type_name = req_type
|
recipient_type_name = req_type
|
||||||
if recipient_type_name == "direct":
|
if recipient_type_name == "direct":
|
||||||
|
@ -221,6 +222,11 @@ def send_message_backend(
|
||||||
raise JsonableError(_("Invalid mirrored message"))
|
raise JsonableError(_("Invalid mirrored message"))
|
||||||
sender = user_profile
|
sender = user_profile
|
||||||
|
|
||||||
|
if read_by_sender is None:
|
||||||
|
# Legacy default: a message you sent from a non-API client is
|
||||||
|
# automatically marked as read for yourself.
|
||||||
|
read_by_sender = client.default_read_by_sender()
|
||||||
|
|
||||||
data: Dict[str, int] = {}
|
data: Dict[str, int] = {}
|
||||||
sent_message_result = check_send_message(
|
sent_message_result = check_send_message(
|
||||||
sender,
|
sender,
|
||||||
|
@ -236,6 +242,7 @@ def send_message_backend(
|
||||||
local_id=local_id,
|
local_id=local_id,
|
||||||
sender_queue_id=queue_id,
|
sender_queue_id=queue_id,
|
||||||
widget_content=widget_content,
|
widget_content=widget_content,
|
||||||
|
read_by_sender=read_by_sender,
|
||||||
)
|
)
|
||||||
data["id"] = sent_message_result.message_id
|
data["id"] = sent_message_result.message_id
|
||||||
if sent_message_result.automatic_new_visibility_policy:
|
if sent_message_result.automatic_new_visibility_policy:
|
||||||
|
|
|
@ -16,7 +16,7 @@ from zerver.lib.response import json_success
|
||||||
from zerver.lib.scheduled_messages import get_undelivered_scheduled_messages
|
from zerver.lib.scheduled_messages import get_undelivered_scheduled_messages
|
||||||
from zerver.lib.timestamp import timestamp_to_datetime
|
from zerver.lib.timestamp import timestamp_to_datetime
|
||||||
from zerver.lib.topic import REQ_topic
|
from zerver.lib.topic import REQ_topic
|
||||||
from zerver.lib.validator import check_int, check_string_in, to_non_negative_int
|
from zerver.lib.validator import check_bool, check_int, check_string_in, to_non_negative_int
|
||||||
from zerver.models import Message, UserProfile
|
from zerver.models import Message, UserProfile
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,6 +115,7 @@ def create_scheduled_message_backend(
|
||||||
topic_name: Optional[str] = REQ_topic(),
|
topic_name: Optional[str] = REQ_topic(),
|
||||||
message_content: str = REQ("content"),
|
message_content: str = REQ("content"),
|
||||||
scheduled_delivery_timestamp: int = REQ(json_validator=check_int),
|
scheduled_delivery_timestamp: int = REQ(json_validator=check_int),
|
||||||
|
read_by_sender: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
recipient_type_name = req_type
|
recipient_type_name = req_type
|
||||||
if recipient_type_name == "direct":
|
if recipient_type_name == "direct":
|
||||||
|
@ -147,5 +148,6 @@ def create_scheduled_message_backend(
|
||||||
deliver_at,
|
deliver_at,
|
||||||
realm=user_profile.realm,
|
realm=user_profile.realm,
|
||||||
forwarder_user_profile=user_profile,
|
forwarder_user_profile=user_profile,
|
||||||
|
read_by_sender=read_by_sender,
|
||||||
)
|
)
|
||||||
return json_success(request, data={"scheduled_message_id": scheduled_message_id})
|
return json_success(request, data={"scheduled_message_id": scheduled_message_id})
|
||||||
|
|
Loading…
Reference in New Issue