Repository: zulip/zulip

Issue:
Exclude milestone events in GitHub integration (at least optionally)
This is a very similar story to:
* #25789 

Basically all the same facts and reasons apply:
* [Here's](https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/zulip-flutter/near/1578205) the start of a ton of spam from this event type.
* Like label/unlabel events, milestone events are liable to be quite spammy because one naturally has one of them just when creating an issue, and may often have bursts of lots of them when doing triage.
* Like label/unlabel events, we know that zero people today are actually relying on these, because the existing messages we show are completely useless in the same way: they repeat the entire issue description, but don't mention the specific milestone that was set.



Discussion:
Hello @zulip/server-integrations members, this issue was labeled with the "area: integrations" label, so you may want to check it out!

<!-- areaLabelAddition -->


Resolving patch:
diff --git a/zerver/lib/webhooks/git.py b/zerver/lib/webhooks/git.py
--- a/zerver/lib/webhooks/git.py
+++ b/zerver/lib/webhooks/git.py
@@ -55,6 +55,9 @@
 )
 ISSUE_LABELED_OR_UNLABELED_MESSAGE_TEMPLATE_WITH_TITLE = "[{user_name}]({user_url}) {action} the {label_name} label {preposition} [Issue #{id} {title}]({url})."
 
+ISSUE_MILESTONED_OR_DEMILESTONED_MESSAGE_TEMPLATE = "[{user_name}]({user_url}) {action} milestone [{milestone_name}]({milestone_url}) {preposition} [issue #{id}]({url})."
+ISSUE_MILESTONED_OR_DEMILESTONED_MESSAGE_TEMPLATE_WITH_TITLE = "[{user_name}]({user_url}) {action} milestone [{milestone_name}]({milestone_url}) {preposition} [issue #{id} {title}]({url})."
+
 PULL_REQUEST_OR_ISSUE_MESSAGE_TEMPLATE = "{user_name} {action} [{type}{id}]({url})"
 PULL_REQUEST_OR_ISSUE_MESSAGE_TEMPLATE_WITH_TITLE = (
     "{user_name} {action} [{type}{id} {title}]({url})"
@@ -302,6 +305,32 @@ def get_issue_labeled_or_unlabeled_event_message(
     return ISSUE_LABELED_OR_UNLABELED_MESSAGE_TEMPLATE.format(**args)
 
 
+def get_issue_milestoned_or_demilestoned_event_message(
+    user_name: str,
+    action: str,
+    url: str,
+    number: int,
+    milestone_name: str,
+    milestone_url: str,
+    user_url: str,
+    title: Optional[str] = None,
+) -> str:
+    args = {
+        "user_name": user_name,
+        "action": action,
+        "url": url,
+        "id": number,
+        "milestone_name": milestone_name,
+        "milestone_url": milestone_url,
+        "user_url": user_url,
+        "title": title,
+        "preposition": "to" if action == "added" else "from",
+    }
+    if title is not None:
+        return ISSUE_MILESTONED_OR_DEMILESTONED_MESSAGE_TEMPLATE_WITH_TITLE.format(**args)
+    return ISSUE_MILESTONED_OR_DEMILESTONED_MESSAGE_TEMPLATE.format(**args)
+
+
 def get_push_tag_event_message(
     user_name: str, tag_name: str, tag_url: Optional[str] = None, action: str = "pushed"
 ) -> str:
diff --git a/zerver/webhooks/github/view.py b/zerver/webhooks/github/view.py
--- a/zerver/webhooks/github/view.py
+++ b/zerver/webhooks/github/view.py
@@ -29,6 +29,7 @@
     get_commits_comment_action_message,
     get_issue_event_message,
     get_issue_labeled_or_unlabeled_event_message,
+    get_issue_milestoned_or_demilestoned_event_message,
     get_pull_request_event_message,
     get_push_commits_event_message,
     get_push_tag_event_message,
@@ -221,6 +222,23 @@ def get_issue_labeled_or_unlabeled_body(helper: Helper) -> str:
     )
 
 
+def get_issue_milestoned_or_demilestoned_body(helper: Helper) -> str:
+    payload = helper.payload
+    include_title = helper.include_title
+    issue = payload["issue"]
+
+    return get_issue_milestoned_or_demilestoned_event_message(
+        user_name=get_sender_name(payload),
+        action="added" if payload["action"].tame(check_string) == "milestoned" else "removed",
+        url=issue["html_url"].tame(check_string),
+        number=issue["number"].tame(check_int),
+        milestone_name=payload["milestone"]["title"].tame(check_string),
+        milestone_url=payload["milestone"]["html_url"].tame(check_string),
+        user_url=get_sender_url(payload),
+        title=issue["title"].tame(check_string) if include_title else None,
+    )
+
+
 def get_fork_body(helper: Helper) -> str:
     payload = helper.payload
     forkee = payload["forkee"]
@@ -728,6 +746,7 @@ def get_subject_based_on_type(payload: WildValue, event: str) -> str:
     "gollum": get_wiki_pages_body,
     "issue_comment": get_issue_comment_body,
     "issue_labeled_or_unlabeled": get_issue_labeled_or_unlabeled_body,
+    "issue_milestoned_or_demilestoned": get_issue_milestoned_or_demilestoned_body,
     "issues": get_issue_body,
     "member": get_member_body,
     "membership": get_membership_body,
@@ -884,6 +903,8 @@ def get_zulip_event_name(
         action = payload["action"].tame(check_string)
         if action in ("labeled", "unlabeled"):
             return "issue_labeled_or_unlabeled"
+        if action in ("milestoned", "demilestoned"):
+            return "issue_milestoned_or_demilestoned"
         else:
             return "issues"
     elif header_event in list(EVENT_FUNCTION_MAPPER.keys()):


Test patch:
diff --git a/zerver/webhooks/github/tests.py b/zerver/webhooks/github/tests.py
--- a/zerver/webhooks/github/tests.py
+++ b/zerver/webhooks/github/tests.py
@@ -193,6 +193,25 @@ def test_issue_unlabeled(self) -> None:
         expected_message = "[sbansal1999](https://github.com/sbansal1999) removed the bug label from [Issue #9](https://github.com/sbansal1999/testing-gh/issues/9)."
         self.check_webhook("issues__unlabeled", expected_topic, expected_message)
 
+    def test_issue_milestoned(self) -> None:
+        expected_topic = "testing-gh / issue #6 This is a sample issue to test GH I..."
+        expected_message = "[sbansal1999](https://github.com/sbansal1999) added milestone [some_random_milestone](https://github.com/sbansal1999/testing-gh/milestone/1) to [issue #6](https://github.com/sbansal1999/testing-gh/issues/6)."
+
+        self.check_webhook("issues__milestoned", expected_topic, expected_message)
+
+    def test_issue_milestoned_with_custom_topic_in_url(self) -> None:
+        self.url = self.build_webhook_url(topic="notifications")
+        expected_topic = "notifications"
+        expected_message = "[sbansal1999](https://github.com/sbansal1999) added milestone [some_random_milestone](https://github.com/sbansal1999/testing-gh/milestone/1) to [issue #6 This is a sample issue to test GH Integration Func](https://github.com/sbansal1999/testing-gh/issues/6)."
+
+        self.check_webhook("issues__milestoned", expected_topic, expected_message)
+
+    def test_issue_demilestoned(self) -> None:
+        expected_topic = "testing-gh / issue #6 This is a sample issue to test GH I..."
+        expected_message = "[sbansal1999](https://github.com/sbansal1999) removed milestone [some_random_milestone](https://github.com/sbansal1999/testing-gh/milestone/1) from [issue #6](https://github.com/sbansal1999/testing-gh/issues/6)."
+
+        self.check_webhook("issues__demilestoned", expected_topic, expected_message)
+
     def test_membership_msg(self) -> None:
         expected_message = (
             "baxterthehacker added [kdaigle](https://github.com/kdaigle) to the Contractors team."