Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
85 lines
3.5 KiB
Python
85 lines
3.5 KiB
Python
from collections.abc import Collection
|
|
from typing import Any, Protocol
|
|
|
|
from django.utils.translation import gettext as _
|
|
|
|
from zerver.lib.exceptions import JsonableError
|
|
from zerver.lib.narrow_helpers import NeverNegatedNarrowTerm
|
|
from zerver.lib.topic import RESOLVED_TOPIC_PREFIX, get_topic_from_message_info
|
|
|
|
# "stream" is a legacy alias for "channel"
|
|
channel_operators: list[str] = ["channel", "stream"]
|
|
# "streams" is a legacy alias for "channels"
|
|
channels_operators: list[str] = ["channels", "streams"]
|
|
|
|
|
|
def check_narrow_for_events(narrow: Collection[NeverNegatedNarrowTerm]) -> None:
|
|
supported_operators = [*channel_operators, "topic", "sender", "is"]
|
|
unsupported_is_operands = ["followed"]
|
|
for narrow_term in narrow:
|
|
operator = narrow_term.operator
|
|
if operator not in supported_operators:
|
|
raise JsonableError(_("Operator {operator} not supported.").format(operator=operator))
|
|
if operator == "is" and narrow_term.operand in unsupported_is_operands:
|
|
raise JsonableError(
|
|
_("Operand {operand} not supported.").format(operand=narrow_term.operand)
|
|
)
|
|
|
|
|
|
class NarrowPredicate(Protocol):
|
|
def __call__(self, *, message: dict[str, Any], flags: list[str]) -> bool: ...
|
|
|
|
|
|
def build_narrow_predicate(
|
|
narrow: Collection[NeverNegatedNarrowTerm],
|
|
) -> NarrowPredicate:
|
|
"""Changes to this function should come with corresponding changes to
|
|
NarrowLibraryTest."""
|
|
check_narrow_for_events(narrow)
|
|
|
|
def narrow_predicate(*, message: dict[str, Any], flags: list[str]) -> bool:
|
|
def satisfies_operator(*, operator: str, operand: str) -> bool:
|
|
if operator in channel_operators:
|
|
if message["type"] != "stream":
|
|
return False
|
|
if operand.lower() != message["display_recipient"].lower():
|
|
return False
|
|
elif operator == "topic":
|
|
if message["type"] != "stream":
|
|
return False
|
|
topic_name = get_topic_from_message_info(message)
|
|
if operand.lower() != topic_name.lower():
|
|
return False
|
|
elif operator == "sender":
|
|
if operand.lower() != message["sender_email"].lower():
|
|
return False
|
|
elif operator == "is" and operand in ["dm", "private"]:
|
|
# "is:private" is a legacy alias for "is:dm"
|
|
if message["type"] != "private":
|
|
return False
|
|
elif operator == "is" and operand == "starred":
|
|
if operand not in flags:
|
|
return False
|
|
elif operator == "is" and operand == "unread":
|
|
if "read" in flags:
|
|
return False
|
|
elif operator == "is" and operand in ["alerted", "mentioned"]:
|
|
if "mentioned" not in flags:
|
|
return False
|
|
elif operator == "is" and operand == "resolved":
|
|
if message["type"] != "stream":
|
|
return False
|
|
topic_name = get_topic_from_message_info(message)
|
|
if not topic_name.startswith(RESOLVED_TOPIC_PREFIX):
|
|
return False
|
|
return True
|
|
|
|
for narrow_term in narrow:
|
|
# TODO: Eventually handle negated narrow terms.
|
|
if not satisfies_operator(operator=narrow_term.operator, operand=narrow_term.operand):
|
|
return False
|
|
|
|
return True
|
|
|
|
return narrow_predicate
|