83 lines
3 KiB
Python
83 lines
3 KiB
Python
from datetime import datetime
|
||
import discord
|
||
|
||
MODALITY_LABELS = {
|
||
"bh_email": "BH Email",
|
||
"personal_email": "Personal Email",
|
||
"bh_text": "BH Text",
|
||
"personal_text": "Personal Text",
|
||
"voice": "Voice",
|
||
"fb_messenger": "FB Messenger",
|
||
"nd_messenger": "ND Messenger",
|
||
"lead_found": "Found Lead",
|
||
"magpie_lead": "Magpie Lead",
|
||
}
|
||
|
||
# Discord's API rejects embed field values of length 0 (min 1, max 1024 chars).
|
||
# Use a zero-width space as a round-trip-safe sentinel for "empty" values so we
|
||
# never send a literal "" to Discord.
|
||
_EMPTY_FIELD_SENTINEL = ""
|
||
|
||
# Hard cap for any field value, with headroom for a truncation marker so the
|
||
# final value never exceeds Discord's 1024-char limit regardless of caller.
|
||
_FIELD_VALUE_MAX = 1024
|
||
_TRUNCATION_MARKER = "\n\n[...truncated for Discord embed limit...]"
|
||
|
||
|
||
def _safe_field_value(value: str) -> str:
|
||
"""Return a Discord-safe embed field value: never empty, never over 1024 chars."""
|
||
if not value:
|
||
return _EMPTY_FIELD_SENTINEL
|
||
if len(value) > _FIELD_VALUE_MAX:
|
||
keep = _FIELD_VALUE_MAX - len(_TRUNCATION_MARKER)
|
||
return value[:keep] + _TRUNCATION_MARKER
|
||
return value
|
||
|
||
|
||
def build_ping_embed(
|
||
*, modality: str, sender_id: str | None, snippet: str, raw_content: str,
|
||
captured_at: datetime, direction: str = "inbound", image_url: str | None = None,
|
||
) -> discord.Embed:
|
||
label = MODALITY_LABELS.get(modality, modality)
|
||
embed = discord.Embed(
|
||
title=f"{label}" + (f" - {sender_id}" if sender_id else ""),
|
||
description=snippet,
|
||
)
|
||
embed.add_field(name="modality", value=_safe_field_value(modality), inline=True)
|
||
embed.add_field(name="direction", value=_safe_field_value(direction), inline=True)
|
||
embed.add_field(name="sender_id", value=_safe_field_value(sender_id or ""), inline=True)
|
||
embed.add_field(
|
||
name="captured_at", value=_safe_field_value(captured_at.isoformat()), inline=True,
|
||
)
|
||
embed.add_field(
|
||
name="raw_content", value=_safe_field_value(raw_content), inline=False,
|
||
)
|
||
# Discord CDN attachment URLs carry a signed expiry (ex/is/hm query params),
|
||
# typically valid ~24h. Fine for same-day triage; a stale link just stops
|
||
# rendering the preview without breaking anything else in the embed.
|
||
if image_url:
|
||
embed.set_image(url=image_url)
|
||
return embed
|
||
|
||
|
||
def parse_ping_embed(embed: discord.Embed) -> dict:
|
||
values = {field.name: field.value for field in embed.fields}
|
||
sender_id = values.get("sender_id")
|
||
if sender_id == _EMPTY_FIELD_SENTINEL:
|
||
sender_id = None
|
||
else:
|
||
sender_id = sender_id or None
|
||
|
||
raw_content = values["raw_content"]
|
||
if raw_content == _EMPTY_FIELD_SENTINEL:
|
||
raw_content = ""
|
||
|
||
direction = values.get("direction") or "inbound"
|
||
|
||
return {
|
||
"modality": values["modality"],
|
||
"sender_id": sender_id,
|
||
"raw_content": raw_content,
|
||
"captured_at": datetime.fromisoformat(values["captured_at"]),
|
||
"direction": direction,
|
||
}
|