We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sentry integration "Test Plugin" failure
Hello, I'm setting up Sentry.io integration and got this error when I tried "Test Plugin" (meaning "Test Outgoing Webhook to Zulip"); I'm assuming it's the response payload that came back from Zulip Cloud:

>"There was an internal error with the Plugin, {\"result\":\"error\",\"msg\":\"The 'Raven SDK' event isn't currently supported by the Sentry webhook\",\"webhook_name\":\"Sentry\",\"event_type\":\"Raven SDK\",\"code\":\"UNSUPPORTED_WEBHOOK_EVENT_TYPE\"}\n"

I'm not sure if there are any events that do work because I'm new to Sentry and not sure how to trigger test events other than the Test Plugin event.

**Zulip Server and web app version:**

- [x] Zulip Cloud (`*.zulipchat.com`)
- [ ] Zulip Server 7.0+
- [ ] Zulip Server 6.0+
- [ ] Zulip Server 5.0 or older
- [ ] Other or not sure


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `zerver/webhooks/sentry/view.py`
Content:
```
1 import logging
2 from datetime import datetime, timezone
3 from typing import Any, Dict, List, Optional, Tuple
4 from urllib.parse import urljoin
5 
6 from django.http import HttpRequest, HttpResponse
7 
8 from zerver.decorator import webhook_view
9 from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
10 from zerver.lib.request import REQ, has_request_variables
11 from zerver.lib.response import json_success
12 from zerver.lib.webhooks.common import check_send_webhook_message
13 from zerver.models import UserProfile
14 
15 DEPRECATED_EXCEPTION_MESSAGE_TEMPLATE = """
16 New [issue]({url}) (level: {level}):
17 
18 ``` quote
19 {message}
20 ```
21 """
22 
23 MESSAGE_EVENT_TEMPLATE = """
24 **New message event:** [{title}]({web_link})
25 ```quote
26 **level:** {level}
27 **timestamp:** {datetime}
28 ```
29 """
30 
31 EXCEPTION_EVENT_TEMPLATE = """
32 **New exception:** [{title}]({web_link})
33 ```quote
34 **level:** {level}
35 **timestamp:** {datetime}
36 **filename:** {filename}
37 ```
38 """
39 
40 EXCEPTION_EVENT_TEMPLATE_WITH_TRACEBACK = (
41     EXCEPTION_EVENT_TEMPLATE
42     + """
43 Traceback:
44 ```{syntax_highlight_as}
45 {pre_context}---> {context_line}{post_context}\
46 ```
47 """
48 )
49 # Because of the \n added at the end of each context element,
50 # this will actually look better in the traceback.
51 
52 ISSUE_CREATED_MESSAGE_TEMPLATE = """
53 **New issue created:** {title}
54 ```quote
55 **level:** {level}
56 **timestamp:** {datetime}
57 **assignee:** {assignee}
58 ```
59 """
60 
61 ISSUE_ASSIGNED_MESSAGE_TEMPLATE = """
62 Issue **{title}** has now been assigned to **{assignee}** by **{actor}**.
63 """
64 
65 ISSUE_RESOLVED_MESSAGE_TEMPLATE = """
66 Issue **{title}** was marked as resolved by **{actor}**.
67 """
68 
69 ISSUE_IGNORED_MESSAGE_TEMPLATE = """
70 Issue **{title}** was ignored by **{actor}**.
71 """
72 
73 # Maps "platform" name provided by Sentry to the Pygments lexer name
74 syntax_highlight_as_map = {
75     "go": "go",
76     "java": "java",
77     "javascript": "javascript",
78     "node": "javascript",
79     "python": "python3",
80     "ruby": "ruby",
81 }
82 
83 
84 def convert_lines_to_traceback_string(lines: Optional[List[str]]) -> str:
85     traceback = ""
86     if lines is not None:
87         for line in lines:
88             if line == "":
89                 traceback += "\n"
90             else:
91                 traceback += f"     {line}\n"
92     return traceback
93 
94 
95 def handle_event_payload(event: Dict[str, Any]) -> Tuple[str, str]:
96     """Handle either an exception type event or a message type event payload."""
97 
98     topic = event["title"]
99     platform_name = event["platform"]
100     syntax_highlight_as = syntax_highlight_as_map.get(platform_name, "")
101     if syntax_highlight_as == "":  # nocoverage
102         logging.info("Unknown Sentry platform: %s", platform_name)
103 
104     # We shouldn't support the officially deprecated Raven series of
105     # Python SDKs.
106     if platform_name == "python" and int(event["version"]) < 7:
107         # The sample event is still an old "version" -- accept it even
108         # though we don't accept events from the old Python SDK.
109         tags = event.get("tags", [])
110         if ["sample_event", "yes"] not in tags:
111             raise UnsupportedWebhookEventTypeError("Raven SDK")
112     context = {
113         "title": topic,
114         "level": event["level"],
115         "web_link": event["web_url"],
116         "datetime": event["datetime"].split(".")[0].replace("T", " "),
117     }
118 
119     if "exception" in event:
120         # The event was triggered by a sentry.capture_exception() call
121         # (in the Python Sentry SDK) or something similar.
122 
123         filename = event["metadata"].get("filename", None)
124 
125         stacktrace = None
126         for value in reversed(event["exception"]["values"]):
127             if "stacktrace" in value:
128                 stacktrace = value["stacktrace"]
129                 break
130 
131         if stacktrace and filename:
132             exception_frame = None
133             for frame in reversed(stacktrace["frames"]):
134                 if frame.get("filename", None) == filename:
135                     exception_frame = frame
136                     break
137 
138             if (
139                 exception_frame
140                 and "context_line" in exception_frame
141                 and exception_frame["context_line"] is not None
142             ):
143                 pre_context = convert_lines_to_traceback_string(
144                     exception_frame.get("pre_context", None)
145                 )
146                 context_line = exception_frame["context_line"] + "\n"
147                 post_context = convert_lines_to_traceback_string(
148                     exception_frame.get("post_context", None)
149                 )
150 
151                 context.update(
152                     syntax_highlight_as=syntax_highlight_as,
153                     filename=filename,
154                     pre_context=pre_context,
155                     context_line=context_line,
156                     post_context=post_context,
157                 )
158 
159                 body = EXCEPTION_EVENT_TEMPLATE_WITH_TRACEBACK.format(**context)
160                 return (topic, body)
161 
162         context.update(filename=filename)  # nocoverage
163         body = EXCEPTION_EVENT_TEMPLATE.format(**context)  # nocoverage
164         return (topic, body)  # nocoverage
165 
166     elif "logentry" in event:
167         # The event was triggered by a sentry.capture_message() call
168         # (in the Python Sentry SDK) or something similar.
169         body = MESSAGE_EVENT_TEMPLATE.format(**context)
170 
171     else:
172         raise UnsupportedWebhookEventTypeError("unknown-event type")
173 
174     return (topic, body)
175 
176 
177 def handle_issue_payload(
178     action: str, issue: Dict[str, Any], actor: Dict[str, Any]
179 ) -> Tuple[str, str]:
180     """Handle either an issue type event."""
181     topic = issue["title"]
182     datetime = issue["lastSeen"].split(".")[0].replace("T", " ")
183 
184     if issue["assignedTo"]:
185         if issue["assignedTo"]["type"] == "team":
186             assignee = "team {}".format(issue["assignedTo"]["name"])
187         else:
188             assignee = issue["assignedTo"]["name"]
189     else:
190         assignee = "No one"
191 
192     if action == "created":
193         context = {
194             "title": topic,
195             "level": issue["level"],
196             "datetime": datetime,
197             "assignee": assignee,
198         }
199         body = ISSUE_CREATED_MESSAGE_TEMPLATE.format(**context)
200 
201     elif action == "resolved":
202         context = {
203             "title": topic,
204             "actor": actor["name"],
205         }
206         body = ISSUE_RESOLVED_MESSAGE_TEMPLATE.format(**context)
207 
208     elif action == "assigned":
209         context = {
210             "title": topic,
211             "assignee": assignee,
212             "actor": actor["name"],
213         }
214         body = ISSUE_ASSIGNED_MESSAGE_TEMPLATE.format(**context)
215 
216     elif action == "ignored":
217         context = {
218             "title": topic,
219             "actor": actor["name"],
220         }
221         body = ISSUE_IGNORED_MESSAGE_TEMPLATE.format(**context)
222 
223     else:
224         raise UnsupportedWebhookEventTypeError("unknown-issue-action type")
225 
226     return (topic, body)
227 
228 
229 def handle_deprecated_payload(payload: Dict[str, Any]) -> Tuple[str, str]:
230     topic = "{}".format(payload.get("project_name"))
231     body = DEPRECATED_EXCEPTION_MESSAGE_TEMPLATE.format(
232         level=payload["level"].upper(),
233         url=payload.get("url"),
234         message=payload.get("message"),
235     )
236     return (topic, body)
237 
238 
239 def transform_webhook_payload(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
240     """Attempt to use webhook payload for the notification.
241 
242     When the integration is configured as a webhook, instead of being added as
243     an internal integration, the payload is slightly different, but has all the
244     required information for sending a notification. We transform this payload to
245     look like the payload from a "properly configured" integration.
246     """
247     event = payload.get("event", {})
248     # deprecated payloads don't have event_id
249     event_id = event.get("event_id")
250     if not event_id:
251         return None
252 
253     event_path = f"events/{event_id}/"
254     event["web_url"] = urljoin(payload["url"], event_path)
255     timestamp = event.get("timestamp", event["received"])
256     event["datetime"] = datetime.fromtimestamp(timestamp, timezone.utc).isoformat(
257         timespec="microseconds"
258     )
259     return payload
260 
261 
262 @webhook_view("Sentry")
263 @has_request_variables
264 def api_sentry_webhook(
265     request: HttpRequest,
266     user_profile: UserProfile,
267     payload: Dict[str, Any] = REQ(argument_type="body"),
268 ) -> HttpResponse:
269     data = payload.get("data", None)
270 
271     if data is None:
272         data = transform_webhook_payload(payload)
273 
274     # We currently support two types of payloads: events and issues.
275     if data:
276         if "event" in data:
277             topic, body = handle_event_payload(data["event"])
278         elif "issue" in data:
279             topic, body = handle_issue_payload(payload["action"], data["issue"], payload["actor"])
280         else:
281             raise UnsupportedWebhookEventTypeError(str(list(data.keys())))
282     else:
283         topic, body = handle_deprecated_payload(payload)
284 
285     check_send_webhook_message(request, user_profile, topic, body)
286     return json_success(request)
287
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/zerver/webhooks/sentry/view.py b/zerver/webhooks/sentry/view.py
--- a/zerver/webhooks/sentry/view.py
+++ b/zerver/webhooks/sentry/view.py
@@ -81,6 +81,15 @@
 }
 
 
+def is_sample_event(event: Dict[str, Any]) -> bool:
+    # This is just a heuristic to detect the sample event, this should
+    # not be used for making important behavior decisions.
+    title = event.get("title", "")
+    if title == "This is an example Python exception":
+        return True
+    return False
+
+
 def convert_lines_to_traceback_string(lines: Optional[List[str]]) -> str:
     traceback = ""
     if lines is not None:
@@ -103,12 +112,10 @@
 
     # We shouldn't support the officially deprecated Raven series of
     # Python SDKs.
-    if platform_name == "python" and int(event["version"]) < 7:
+    if platform_name == "python" and int(event["version"]) < 7 and not is_sample_event(event):
         # The sample event is still an old "version" -- accept it even
         # though we don't accept events from the old Python SDK.
-        tags = event.get("tags", [])
-        if ["sample_event", "yes"] not in tags:
-            raise UnsupportedWebhookEventTypeError("Raven SDK")
+        raise UnsupportedWebhookEventTypeError("Raven SDK")
     context = {
         "title": subject,
         "level": event["level"],

{"golden_diff": "diff --git a/zerver/webhooks/sentry/view.py b/zerver/webhooks/sentry/view.py\n--- a/zerver/webhooks/sentry/view.py\n+++ b/zerver/webhooks/sentry/view.py\n@@ -81,6 +81,15 @@\n }\n \n \n+def is_sample_event(event: Dict[str, Any]) -> bool:\n+    # This is just a heuristic to detect the sample event, this should\n+    # not be used for making important behavior decisions.\n+    title = event.get(\"title\", \"\")\n+    if title == \"This is an example Python exception\":\n+        return True\n+    return False\n+\n+\n def convert_lines_to_traceback_string(lines: Optional[List[str]]) -> str:\n     traceback = \"\"\n     if lines is not None:\n@@ -103,12 +112,10 @@\n \n     # We shouldn't support the officially deprecated Raven series of\n     # Python SDKs.\n-    if platform_name == \"python\" and int(event[\"version\"]) < 7:\n+    if platform_name == \"python\" and int(event[\"version\"]) < 7 and not is_sample_event(event):\n         # The sample event is still an old \"version\" -- accept it even\n         # though we don't accept events from the old Python SDK.\n-        tags = event.get(\"tags\", [])\n-        if [\"sample_event\", \"yes\"] not in tags:\n-            raise UnsupportedWebhookEventTypeError(\"Raven SDK\")\n+        raise UnsupportedWebhookEventTypeError(\"Raven SDK\")\n     context = {\n         \"title\": subject,\n         \"level\": event[\"level\"],\n", "issue": "Sentry integration \"Test Plugin\" failure\nHello, I'm setting up Sentry.io integration and got this error when I tried \"Test Plugin\" (meaning \"Test Outgoing Webhook to Zulip\"); I'm assuming it's the response payload that came back from Zulip Cloud:\r\n\r\n>\"There was an internal error with the Plugin, {\\\"result\\\":\\\"error\\\",\\\"msg\\\":\\\"The 'Raven SDK' event isn't currently supported by the Sentry webhook\\\",\\\"webhook_name\\\":\\\"Sentry\\\",\\\"event_type\\\":\\\"Raven SDK\\\",\\\"code\\\":\\\"UNSUPPORTED_WEBHOOK_EVENT_TYPE\\\"}\\n\"\r\n\r\nI'm not sure if there are any events that do work because I'm new to Sentry and not sure how to trigger test events other than the Test Plugin event.\r\n\r\n**Zulip Server and web app version:**\r\n\r\n- [x] Zulip Cloud (`*.zulipchat.com`)\r\n- [ ] Zulip Server 7.0+\r\n- [ ] Zulip Server 6.0+\r\n- [ ] Zulip Server 5.0 or older\r\n- [ ] Other or not sure\r\n\n", "before_files": [{"content": "import logging\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict, List, Optional, Tuple\nfrom urllib.parse import urljoin\n\nfrom django.http import HttpRequest, HttpResponse\n\nfrom zerver.decorator import webhook_view\nfrom zerver.lib.exceptions import UnsupportedWebhookEventTypeError\nfrom zerver.lib.request import REQ, has_request_variables\nfrom zerver.lib.response import json_success\nfrom zerver.lib.webhooks.common import check_send_webhook_message\nfrom zerver.models import UserProfile\n\nDEPRECATED_EXCEPTION_MESSAGE_TEMPLATE = \"\"\"\nNew [issue]({url}) (level: {level}):\n\n``` quote\n{message}\n```\n\"\"\"\n\nMESSAGE_EVENT_TEMPLATE = \"\"\"\n**New message event:** [{title}]({web_link})\n```quote\n**level:** {level}\n**timestamp:** {datetime}\n```\n\"\"\"\n\nEXCEPTION_EVENT_TEMPLATE = \"\"\"\n**New exception:** [{title}]({web_link})\n```quote\n**level:** {level}\n**timestamp:** {datetime}\n**filename:** {filename}\n```\n\"\"\"\n\nEXCEPTION_EVENT_TEMPLATE_WITH_TRACEBACK = (\n    EXCEPTION_EVENT_TEMPLATE\n    + \"\"\"\nTraceback:\n```{syntax_highlight_as}\n{pre_context}---> {context_line}{post_context}\\\n```\n\"\"\"\n)\n# Because of the \\n added at the end of each context element,\n# this will actually look better in the traceback.\n\nISSUE_CREATED_MESSAGE_TEMPLATE = \"\"\"\n**New issue created:** {title}\n```quote\n**level:** {level}\n**timestamp:** {datetime}\n**assignee:** {assignee}\n```\n\"\"\"\n\nISSUE_ASSIGNED_MESSAGE_TEMPLATE = \"\"\"\nIssue **{title}** has now been assigned to **{assignee}** by **{actor}**.\n\"\"\"\n\nISSUE_RESOLVED_MESSAGE_TEMPLATE = \"\"\"\nIssue **{title}** was marked as resolved by **{actor}**.\n\"\"\"\n\nISSUE_IGNORED_MESSAGE_TEMPLATE = \"\"\"\nIssue **{title}** was ignored by **{actor}**.\n\"\"\"\n\n# Maps \"platform\" name provided by Sentry to the Pygments lexer name\nsyntax_highlight_as_map = {\n    \"go\": \"go\",\n    \"java\": \"java\",\n    \"javascript\": \"javascript\",\n    \"node\": \"javascript\",\n    \"python\": \"python3\",\n    \"ruby\": \"ruby\",\n}\n\n\ndef convert_lines_to_traceback_string(lines: Optional[List[str]]) -> str:\n    traceback = \"\"\n    if lines is not None:\n        for line in lines:\n            if line == \"\":\n                traceback += \"\\n\"\n            else:\n                traceback += f\"     {line}\\n\"\n    return traceback\n\n\ndef handle_event_payload(event: Dict[str, Any]) -> Tuple[str, str]:\n    \"\"\"Handle either an exception type event or a message type event payload.\"\"\"\n\n    topic = event[\"title\"]\n    platform_name = event[\"platform\"]\n    syntax_highlight_as = syntax_highlight_as_map.get(platform_name, \"\")\n    if syntax_highlight_as == \"\":  # nocoverage\n        logging.info(\"Unknown Sentry platform: %s\", platform_name)\n\n    # We shouldn't support the officially deprecated Raven series of\n    # Python SDKs.\n    if platform_name == \"python\" and int(event[\"version\"]) < 7:\n        # The sample event is still an old \"version\" -- accept it even\n        # though we don't accept events from the old Python SDK.\n        tags = event.get(\"tags\", [])\n        if [\"sample_event\", \"yes\"] not in tags:\n            raise UnsupportedWebhookEventTypeError(\"Raven SDK\")\n    context = {\n        \"title\": topic,\n        \"level\": event[\"level\"],\n        \"web_link\": event[\"web_url\"],\n        \"datetime\": event[\"datetime\"].split(\".\")[0].replace(\"T\", \" \"),\n    }\n\n    if \"exception\" in event:\n        # The event was triggered by a sentry.capture_exception() call\n        # (in the Python Sentry SDK) or something similar.\n\n        filename = event[\"metadata\"].get(\"filename\", None)\n\n        stacktrace = None\n        for value in reversed(event[\"exception\"][\"values\"]):\n            if \"stacktrace\" in value:\n                stacktrace = value[\"stacktrace\"]\n                break\n\n        if stacktrace and filename:\n            exception_frame = None\n            for frame in reversed(stacktrace[\"frames\"]):\n                if frame.get(\"filename\", None) == filename:\n                    exception_frame = frame\n                    break\n\n            if (\n                exception_frame\n                and \"context_line\" in exception_frame\n                and exception_frame[\"context_line\"] is not None\n            ):\n                pre_context = convert_lines_to_traceback_string(\n                    exception_frame.get(\"pre_context\", None)\n                )\n                context_line = exception_frame[\"context_line\"] + \"\\n\"\n                post_context = convert_lines_to_traceback_string(\n                    exception_frame.get(\"post_context\", None)\n                )\n\n                context.update(\n                    syntax_highlight_as=syntax_highlight_as,\n                    filename=filename,\n                    pre_context=pre_context,\n                    context_line=context_line,\n                    post_context=post_context,\n                )\n\n                body = EXCEPTION_EVENT_TEMPLATE_WITH_TRACEBACK.format(**context)\n                return (topic, body)\n\n        context.update(filename=filename)  # nocoverage\n        body = EXCEPTION_EVENT_TEMPLATE.format(**context)  # nocoverage\n        return (topic, body)  # nocoverage\n\n    elif \"logentry\" in event:\n        # The event was triggered by a sentry.capture_message() call\n        # (in the Python Sentry SDK) or something similar.\n        body = MESSAGE_EVENT_TEMPLATE.format(**context)\n\n    else:\n        raise UnsupportedWebhookEventTypeError(\"unknown-event type\")\n\n    return (topic, body)\n\n\ndef handle_issue_payload(\n    action: str, issue: Dict[str, Any], actor: Dict[str, Any]\n) -> Tuple[str, str]:\n    \"\"\"Handle either an issue type event.\"\"\"\n    topic = issue[\"title\"]\n    datetime = issue[\"lastSeen\"].split(\".\")[0].replace(\"T\", \" \")\n\n    if issue[\"assignedTo\"]:\n        if issue[\"assignedTo\"][\"type\"] == \"team\":\n            assignee = \"team {}\".format(issue[\"assignedTo\"][\"name\"])\n        else:\n            assignee = issue[\"assignedTo\"][\"name\"]\n    else:\n        assignee = \"No one\"\n\n    if action == \"created\":\n        context = {\n            \"title\": topic,\n            \"level\": issue[\"level\"],\n            \"datetime\": datetime,\n            \"assignee\": assignee,\n        }\n        body = ISSUE_CREATED_MESSAGE_TEMPLATE.format(**context)\n\n    elif action == \"resolved\":\n        context = {\n            \"title\": topic,\n            \"actor\": actor[\"name\"],\n        }\n        body = ISSUE_RESOLVED_MESSAGE_TEMPLATE.format(**context)\n\n    elif action == \"assigned\":\n        context = {\n            \"title\": topic,\n            \"assignee\": assignee,\n            \"actor\": actor[\"name\"],\n        }\n        body = ISSUE_ASSIGNED_MESSAGE_TEMPLATE.format(**context)\n\n    elif action == \"ignored\":\n        context = {\n            \"title\": topic,\n            \"actor\": actor[\"name\"],\n        }\n        body = ISSUE_IGNORED_MESSAGE_TEMPLATE.format(**context)\n\n    else:\n        raise UnsupportedWebhookEventTypeError(\"unknown-issue-action type\")\n\n    return (topic, body)\n\n\ndef handle_deprecated_payload(payload: Dict[str, Any]) -> Tuple[str, str]:\n    topic = \"{}\".format(payload.get(\"project_name\"))\n    body = DEPRECATED_EXCEPTION_MESSAGE_TEMPLATE.format(\n        level=payload[\"level\"].upper(),\n        url=payload.get(\"url\"),\n        message=payload.get(\"message\"),\n    )\n    return (topic, body)\n\n\ndef transform_webhook_payload(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:\n    \"\"\"Attempt to use webhook payload for the notification.\n\n    When the integration is configured as a webhook, instead of being added as\n    an internal integration, the payload is slightly different, but has all the\n    required information for sending a notification. We transform this payload to\n    look like the payload from a \"properly configured\" integration.\n    \"\"\"\n    event = payload.get(\"event\", {})\n    # deprecated payloads don't have event_id\n    event_id = event.get(\"event_id\")\n    if not event_id:\n        return None\n\n    event_path = f\"events/{event_id}/\"\n    event[\"web_url\"] = urljoin(payload[\"url\"], event_path)\n    timestamp = event.get(\"timestamp\", event[\"received\"])\n    event[\"datetime\"] = datetime.fromtimestamp(timestamp, timezone.utc).isoformat(\n        timespec=\"microseconds\"\n    )\n    return payload\n\n\n@webhook_view(\"Sentry\")\n@has_request_variables\ndef api_sentry_webhook(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    payload: Dict[str, Any] = REQ(argument_type=\"body\"),\n) -> HttpResponse:\n    data = payload.get(\"data\", None)\n\n    if data is None:\n        data = transform_webhook_payload(payload)\n\n    # We currently support two types of payloads: events and issues.\n    if data:\n        if \"event\" in data:\n            topic, body = handle_event_payload(data[\"event\"])\n        elif \"issue\" in data:\n            topic, body = handle_issue_payload(payload[\"action\"], data[\"issue\"], payload[\"actor\"])\n        else:\n            raise UnsupportedWebhookEventTypeError(str(list(data.keys())))\n    else:\n        topic, body = handle_deprecated_payload(payload)\n\n    check_send_webhook_message(request, user_profile, topic, body)\n    return json_success(request)\n", "path": "zerver/webhooks/sentry/view.py"}], "after_files": [{"content": "import logging\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict, List, Optional, Tuple\nfrom urllib.parse import urljoin\n\nfrom django.http import HttpRequest, HttpResponse\n\nfrom zerver.decorator import webhook_view\nfrom zerver.lib.exceptions import UnsupportedWebhookEventTypeError\nfrom zerver.lib.request import REQ, has_request_variables\nfrom zerver.lib.response import json_success\nfrom zerver.lib.webhooks.common import check_send_webhook_message\nfrom zerver.models import UserProfile\n\nDEPRECATED_EXCEPTION_MESSAGE_TEMPLATE = \"\"\"\nNew [issue]({url}) (level: {level}):\n\n``` quote\n{message}\n```\n\"\"\"\n\nMESSAGE_EVENT_TEMPLATE = \"\"\"\n**New message event:** [{title}]({web_link})\n```quote\n**level:** {level}\n**timestamp:** {datetime}\n```\n\"\"\"\n\nEXCEPTION_EVENT_TEMPLATE = \"\"\"\n**New exception:** [{title}]({web_link})\n```quote\n**level:** {level}\n**timestamp:** {datetime}\n**filename:** {filename}\n```\n\"\"\"\n\nEXCEPTION_EVENT_TEMPLATE_WITH_TRACEBACK = (\n    EXCEPTION_EVENT_TEMPLATE\n    + \"\"\"\nTraceback:\n```{syntax_highlight_as}\n{pre_context}---> {context_line}{post_context}\\\n```\n\"\"\"\n)\n# Because of the \\n added at the end of each context element,\n# this will actually look better in the traceback.\n\nISSUE_CREATED_MESSAGE_TEMPLATE = \"\"\"\n**New issue created:** {title}\n```quote\n**level:** {level}\n**timestamp:** {datetime}\n**assignee:** {assignee}\n```\n\"\"\"\n\nISSUE_ASSIGNED_MESSAGE_TEMPLATE = \"\"\"\nIssue **{title}** has now been assigned to **{assignee}** by **{actor}**.\n\"\"\"\n\nISSUE_RESOLVED_MESSAGE_TEMPLATE = \"\"\"\nIssue **{title}** was marked as resolved by **{actor}**.\n\"\"\"\n\nISSUE_IGNORED_MESSAGE_TEMPLATE = \"\"\"\nIssue **{title}** was ignored by **{actor}**.\n\"\"\"\n\n# Maps \"platform\" name provided by Sentry to the Pygments lexer name\nsyntax_highlight_as_map = {\n    \"go\": \"go\",\n    \"java\": \"java\",\n    \"javascript\": \"javascript\",\n    \"node\": \"javascript\",\n    \"python\": \"python3\",\n    \"ruby\": \"ruby\",\n}\n\n\ndef is_sample_event(event: Dict[str, Any]) -> bool:\n    # This is just a heuristic to detect the sample event, this should\n    # not be used for making important behavior decisions.\n    title = event.get(\"title\", \"\")\n    if title == \"This is an example Python exception\":\n        return True\n    return False\n\n\ndef convert_lines_to_traceback_string(lines: Optional[List[str]]) -> str:\n    traceback = \"\"\n    if lines is not None:\n        for line in lines:\n            if line == \"\":\n                traceback += \"\\n\"\n            else:\n                traceback += f\"     {line}\\n\"\n    return traceback\n\n\ndef handle_event_payload(event: Dict[str, Any]) -> Tuple[str, str]:\n    \"\"\"Handle either an exception type event or a message type event payload.\"\"\"\n\n    subject = event[\"title\"]\n    platform_name = event[\"platform\"]\n    syntax_highlight_as = syntax_highlight_as_map.get(platform_name, \"\")\n    if syntax_highlight_as == \"\":  # nocoverage\n        logging.info(\"Unknown Sentry platform: %s\", platform_name)\n\n    # We shouldn't support the officially deprecated Raven series of\n    # Python SDKs.\n    if platform_name == \"python\" and int(event[\"version\"]) < 7 and not is_sample_event(event):\n        # The sample event is still an old \"version\" -- accept it even\n        # though we don't accept events from the old Python SDK.\n        raise UnsupportedWebhookEventTypeError(\"Raven SDK\")\n    context = {\n        \"title\": subject,\n        \"level\": event[\"level\"],\n        \"web_link\": event[\"web_url\"],\n        \"datetime\": event[\"datetime\"].split(\".\")[0].replace(\"T\", \" \"),\n    }\n\n    if \"exception\" in event:\n        # The event was triggered by a sentry.capture_exception() call\n        # (in the Python Sentry SDK) or something similar.\n\n        filename = event[\"metadata\"].get(\"filename\", None)\n\n        stacktrace = None\n        for value in reversed(event[\"exception\"][\"values\"]):\n            if \"stacktrace\" in value:\n                stacktrace = value[\"stacktrace\"]\n                break\n\n        if stacktrace and filename:\n            exception_frame = None\n            for frame in reversed(stacktrace[\"frames\"]):\n                if frame.get(\"filename\", None) == filename:\n                    exception_frame = frame\n                    break\n\n            if (\n                exception_frame\n                and \"context_line\" in exception_frame\n                and exception_frame[\"context_line\"] is not None\n            ):\n                pre_context = convert_lines_to_traceback_string(\n                    exception_frame.get(\"pre_context\", None)\n                )\n                context_line = exception_frame[\"context_line\"] + \"\\n\"\n                post_context = convert_lines_to_traceback_string(\n                    exception_frame.get(\"post_context\", None)\n                )\n\n                context.update(\n                    syntax_highlight_as=syntax_highlight_as,\n                    filename=filename,\n                    pre_context=pre_context,\n                    context_line=context_line,\n                    post_context=post_context,\n                )\n\n                body = EXCEPTION_EVENT_TEMPLATE_WITH_TRACEBACK.format(**context)\n                return (subject, body)\n\n        context.update(filename=filename)  # nocoverage\n        body = EXCEPTION_EVENT_TEMPLATE.format(**context)  # nocoverage\n        return (subject, body)  # nocoverage\n\n    elif \"logentry\" in event:\n        # The event was triggered by a sentry.capture_message() call\n        # (in the Python Sentry SDK) or something similar.\n        body = MESSAGE_EVENT_TEMPLATE.format(**context)\n\n    else:\n        raise UnsupportedWebhookEventTypeError(\"unknown-event type\")\n\n    return (subject, body)\n\n\ndef handle_issue_payload(\n    action: str, issue: Dict[str, Any], actor: Dict[str, Any]\n) -> Tuple[str, str]:\n    \"\"\"Handle either an issue type event.\"\"\"\n    subject = issue[\"title\"]\n    datetime = issue[\"lastSeen\"].split(\".\")[0].replace(\"T\", \" \")\n\n    if issue[\"assignedTo\"]:\n        if issue[\"assignedTo\"][\"type\"] == \"team\":\n            assignee = \"team {}\".format(issue[\"assignedTo\"][\"name\"])\n        else:\n            assignee = issue[\"assignedTo\"][\"name\"]\n    else:\n        assignee = \"No one\"\n\n    if action == \"created\":\n        context = {\n            \"title\": subject,\n            \"level\": issue[\"level\"],\n            \"datetime\": datetime,\n            \"assignee\": assignee,\n        }\n        body = ISSUE_CREATED_MESSAGE_TEMPLATE.format(**context)\n\n    elif action == \"resolved\":\n        context = {\n            \"title\": subject,\n            \"actor\": actor[\"name\"],\n        }\n        body = ISSUE_RESOLVED_MESSAGE_TEMPLATE.format(**context)\n\n    elif action == \"assigned\":\n        context = {\n            \"title\": subject,\n            \"assignee\": assignee,\n            \"actor\": actor[\"name\"],\n        }\n        body = ISSUE_ASSIGNED_MESSAGE_TEMPLATE.format(**context)\n\n    elif action == \"ignored\":\n        context = {\n            \"title\": subject,\n            \"actor\": actor[\"name\"],\n        }\n        body = ISSUE_IGNORED_MESSAGE_TEMPLATE.format(**context)\n\n    else:\n        raise UnsupportedWebhookEventTypeError(\"unknown-issue-action type\")\n\n    return (subject, body)\n\n\ndef handle_deprecated_payload(payload: Dict[str, Any]) -> Tuple[str, str]:\n    subject = \"{}\".format(payload.get(\"project_name\"))\n    body = DEPRECATED_EXCEPTION_MESSAGE_TEMPLATE.format(\n        level=payload[\"level\"].upper(),\n        url=payload.get(\"url\"),\n        message=payload.get(\"message\"),\n    )\n    return (subject, body)\n\n\ndef transform_webhook_payload(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:\n    \"\"\"Attempt to use webhook payload for the notification.\n\n    When the integration is configured as a webhook, instead of being added as\n    an internal integration, the payload is slightly different, but has all the\n    required information for sending a notification. We transform this payload to\n    look like the payload from a \"properly configured\" integration.\n    \"\"\"\n    event = payload.get(\"event\", {})\n    # deprecated payloads don't have event_id\n    event_id = event.get(\"event_id\")\n    if not event_id:\n        return None\n\n    event_path = f\"events/{event_id}/\"\n    event[\"web_url\"] = urljoin(payload[\"url\"], event_path)\n    timestamp = event.get(\"timestamp\", event[\"received\"])\n    event[\"datetime\"] = datetime.fromtimestamp(timestamp, timezone.utc).isoformat(\n        timespec=\"microseconds\"\n    )\n    return payload\n\n\n@webhook_view(\"Sentry\")\n@has_request_variables\ndef api_sentry_webhook(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    payload: Dict[str, Any] = REQ(argument_type=\"body\"),\n) -> HttpResponse:\n    data = payload.get(\"data\", None)\n\n    if data is None:\n        data = transform_webhook_payload(payload)\n\n    # We currently support two types of payloads: events and issues.\n    if data:\n        if \"event\" in data:\n            subject, body = handle_event_payload(data[\"event\"])\n        elif \"issue\" in data:\n            subject, body = handle_issue_payload(payload[\"action\"], data[\"issue\"], payload[\"actor\"])\n        else:\n            raise UnsupportedWebhookEventTypeError(str(list(data.keys())))\n    else:\n        subject, body = handle_deprecated_payload(payload)\n\n    check_send_webhook_message(request, user_profile, subject, body)\n    return json_success(request)\n", "path": "zerver/webhooks/sentry/view.py"}]}