From d6a676ad8e285eee6306a5f09ea22e48df359d55 Mon Sep 17 00:00:00 2001 From: mintaka Date: Mon, 9 Nov 2026 07:18:54 -0500 Subject: [PATCH] fix(discord): name a forum post from the caption with media (RIG-8690) Co-authored-by: Matt Wilkinson --- plugins/platforms/discord/adapter.py | 2 +- .../test_discord_send_message_caption.py | 50 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index ca46a237d628..07a7da0d31a0 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -7234,7 +7234,7 @@ async def _standalone_send( else: # Forum channels (type 15) reject POST /messages — create a thread post. if await _standalone_is_forum(aiohttp, chat_id, json_headers, _sess_kw, _req_kw): - thread_name = _derive_forum_thread_name(message) + thread_name = _derive_forum_thread_name(caption or message) thread_url = f"https://discord.com/api/v10/channels/{chat_id}/threads" # Filter readable media first to pick JSON vs multipart before opening a session. valid_media = [] diff --git a/tests/tools/test_discord_send_message_caption.py b/tests/tools/test_discord_send_message_caption.py index 02e102904fd5..abfa48477e69 100644 --- a/tests/tools/test_discord_send_message_caption.py +++ b/tests/tools/test_discord_send_message_caption.py @@ -13,6 +13,8 @@ from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock, patch +import pytest + from plugins.platforms.discord.adapter import _remember_channel_is_forum, _standalone_send @@ -66,8 +68,8 @@ def _tmpfile(suffix): return f.name -def _payload_json_content(form_data): - """Extract the 'content' from a FormData's payload_json field, if any.""" +def _payload_json(form_data): + """Extract Discord's multipart payload_json object, if present.""" for field in getattr(form_data, "_fields", []): # aiohttp FormData stores (type_options_dict, headers, value) try: @@ -76,10 +78,52 @@ def _payload_json_content(form_data): except (IndexError, TypeError): continue if type_opts.get("name") == "payload_json": - return json.loads(value).get("content") + return json.loads(value) return None +def _payload_json_content(form_data): + """Extract the message content from a JSON or multipart POST payload.""" + payload = _payload_json(form_data) or {} + return payload.get("message", payload).get("content") + + +@pytest.mark.parametrize( + ("message", "caption"), + [ + ("", "Health check-in: Sun Nov 8\nmore"), + ("Health check-in: Sun Nov 8\nmore", ""), + ], +) +def test_forum_thread_name_uses_caption_or_message(message, caption): + chat_id = "999000333" + _remember_channel_is_forum(chat_id, True) + img = _tmpfile(".png") + try: + session_ctx, calls = _session_with( + [_resp(200, {"id": "thread-1", "message": {"id": "starter-1"}})] + ) + with patch("aiohttp.ClientSession", return_value=session_ctx): + result = asyncio.run( + _standalone_send( + _pconfig(), + chat_id, + message, + media_files=[(img, False)], + caption=caption or None, + ) + ) + assert result["success"] is True + assert len(calls) == 1 + url, _json, data = calls[0] + assert url.endswith("/threads") + payload = _payload_json(data) + assert payload["name"] == "Health check-in: Sun Nov 8" + assert payload["message"]["content"] == (caption or message) + finally: + os.unlink(img) + + def test_caption_rides_media_non_forum(): chat_id = "999000111" _remember_channel_is_forum(chat_id, False) # avoid the live GET probe