slack_incoming: Support "fields" in "section"s.

This is a best-effort rendering of the "fields" of Slack incoming
hooks, which Slack renders in two columns.  We approximate them in a
Markdown table, with some minor in-place replacements.

Fixes #22228.
This commit is contained in:
Alex Vandiver 2023-01-04 20:08:26 +00:00 committed by Tim Abbott
parent 4dc57dadd6
commit 1b692984ce
2 changed files with 34 additions and 5 deletions

View File

@ -74,6 +74,9 @@ Danny Torrence left the following *review* for your property:
[Overlook Hotel](https://google.com) \n :star: \n Doors had too many axe holes, guest in room 237 was far too rowdy, whole place felt stuck in the 1920s.
[Haunted hotel image](https://is5-ssl.mzstatic.com/image/thumb/Purple3/v4/d3/72/5c/d3725c8f-c642-5d69-1904-aa36e4297885/source/256x256bb.jpg)
**Average Rating**
1.0
""".strip()
self.check_webhook(
@ -90,6 +93,9 @@ Danny Torrence left the following review for your property:
[Overlook Hotel](https://example.com) \n :star: \n Doors had too many axe holes, guest in room 237 was far too rowdy, whole place felt stuck in the 1920s.
[Haunted hotel image](https://is5-ssl.mzstatic.com/image/thumb/Purple3/v4/d3/72/5c/d3725c8f-c642-5d69-1904-aa36e4297885/source/256x256bb.jpg)
**Average Rating**
1.0
""".strip()
self.check_webhook(
@ -175,6 +181,12 @@ This is a section block with an accessory image.
[cute cat](https://pbs.twimg.com/profile_images/625633822235693056/lNGUneLX_400x400.jpg)
This is a section block with a button.
| | |
|-|-|
| one | two |
| three | four |
| five | |
""".strip()
self.check_webhook(

View File

@ -1,5 +1,6 @@
# Webhooks for external integrations.
import re
from itertools import zip_longest
from typing import Literal, Optional, TypedDict, cast
from django.http import HttpRequest, HttpResponse
@ -14,6 +15,7 @@ from zerver.lib.validator import (
WildValue,
check_dict,
check_int,
check_list,
check_string,
check_string_in,
check_url,
@ -122,11 +124,26 @@ def render_block(block: WildValue) -> str:
pieces.append(render_block_element(block["accessory"]))
if "fields" in block:
# TODO -- these should be rendered in two columns,
# left-to-right. We could render them sequentially,
# except some may be Title1 / Title2 / value1 / value2,
# which would be nonsensical when rendered sequentially.
pass
fields = block["fields"].tame(check_list(check_text_block()))
if len(fields) == 1:
# Special-case a single field to display a bit more
# nicely, without extraneous borders and limitations
# on its contents.
pieces.append(fields[0]["text"])
else:
# It is not possible to have newlines in a table, nor
# escape the pipes that make it up; replace them with
# whitespace.
field_text = [f["text"].replace("\n", " ").replace("|", " ") for f in fields]
# Because Slack formats this as two columns, but not
# necessarily a table with a bold header, we emit a
# blank header row first.
table = "| | |\n|-|-|\n"
# Then take the fields two-at-a-time to make the table
iters = [iter(field_text)] * 2
for left, right in zip_longest(*iters, fillvalue=""):
table += f"| {left} | {right} |\n"
pieces.append(table)
return "\n\n".join(piece.strip() for piece in pieces if piece.strip() != "")